Skip to content

Commit

Permalink
[1.3.1] Update Thicken Test
Browse files Browse the repository at this point in the history
  • Loading branch information
Nialixus committed Nov 3, 2024
1 parent 4d697c3 commit 92723d4
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 76 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,7 @@
* Update test coverage

## 1.1.2
* Update logo in readme
* Update logo in readme

## 1.1.3
* Update Thicken Test
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Add this line to your pubspec.yaml.

```yaml
dependencies:
thicken: ^1.1.2
thicken: ^1.1.3
```
## Usage
Expand Down Expand Up @@ -50,6 +50,7 @@ Thicken(

| Property | Purpose |
| ------------------ | ----------------------------------------------------------------------------------------------------------------- |
| pixelRatio | The sharpness quality of the stroke. |
| thickness | The amount of thickness applied. _**(It is not recommended to set thickness greater than 1.0)**_ |
| child | The child widget that will be thickened as multiple layers. |

Expand Down
139 changes: 84 additions & 55 deletions lib/thicken.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ library thicken;
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

/// A widget that creates a thick visual effect by stacking multiple
Expand Down Expand Up @@ -50,22 +51,19 @@ import 'package:flutter/widgets.dart';
/// _**(It is not recommended to set thickness greater than 1.0)**_
///
/// [child] : The widget that will be "thickened" by drawing multiple layers.
class Thicken extends StatelessWidget {
class Thicken extends StatefulWidget {
/// Creates a [Thicken] widget.
///
/// The [thickness] parameter defines how many layers of the child widget
/// will be drawn, as the [pixelRatio] parameter is used to define the sharpness of the strokes,
/// and the [child] is the widget that will be thickened.
Thicken({
const Thicken({
super.key,
this.pixelRatio = 1.0,
this.pixelRatio = 2.0,
required this.thickness,
required this.child,
});

/// The key used to convert the [child] widget to an image.
late final _key = GlobalKey(debugLabel: 'thicken.$key');

/// The amount of thickness applied. The thickness controls how many layers
/// of the [child] widget are created. A higher [thickness] value creates more
/// layers and larger translations between them.
Expand All @@ -89,58 +87,89 @@ class Thicken extends StatelessWidget {
return 3 + (range * 2);
}

@override
State<Thicken> createState() => _ThickenState();
}

class _ThickenState extends State<Thicken> {
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: () async {
try {
// Wait until after the frame is built
await Future.delayed(Duration.zero);

final boundary = _key.currentContext?.findRenderObject();
if (boundary is RenderRepaintBoundary) {
final image = await boundary.toImage(pixelRatio: pixelRatio);
final byte = await image.toByteData(
format: ui.ImageByteFormat.png,
);

return byte?.buffer.asUint8List();
}

return null;
} catch (e) {
return null;
}
}(),
builder: (builder, snapshot) {
return Stack(
children: [
...List.generate(
layers * layers,
(index) {
final indexX = index % layers;
final indexY = index ~/ layers;
final offsetX = (1 - (2 * indexX / layers)) * thickness;
final offsetY = (1 - (2 * indexY / layers)) * thickness;

if (index + indexY == 0) return const SizedBox();
return Positioned.fill(
child: Transform.translate(
if (widget.thickness == 0.0) return child;
return Stack(
children: [
if (image != null)
...List.generate(
widget.layers * widget.layers,
(index) {
final indexX = index % widget.layers;
final indexY = index ~/ widget.layers;
final offsetX =
(1 - (2 * indexX / widget.layers)) * widget.thickness;
final offsetY =
(1 - (2 * indexY / widget.layers)) * widget.thickness;

if (index + indexY == 0) return const SizedBox();
return Positioned.fill(
child: Transform.translate(
offset: Offset(offsetX, offsetY),
child: snapshot.data != null
? Image.memory(snapshot.data!)
: child,
),
);
},
),
RepaintBoundary(
key: _key,
child: child,
),
],
);
},
child: Image.memory(image!)),
);
},
),
RepaintBoundary(
key: _key,
child: child,
),
],
);
}

/// The child widget that will be thickened with multiple layers.
late final Widget child = widget.child;

/// The key used to convert the [child] widget to an image.
late final GlobalKey _key;

/// The generated image of the [child] widget.
Uint8List? image;

/// Converts the [child] widget to an image.
///
/// Returns a [Future] that completes with the [Uint8List] of the image.
Future<Uint8List?> toBitmap() async {
try {
// Wait until after the frame is built
await Future.delayed(Duration.zero);

final boundary = _key.currentContext?.findRenderObject();
if (boundary is RenderRepaintBoundary) {
final image = await boundary.toImage(pixelRatio: widget.pixelRatio);
final byte = await image.toByteData(
format: ui.ImageByteFormat.png,
);

return byte?.buffer.asUint8List();
}

return null;
} catch (e) {
return null;
}
}

@override
void initState() {
super.initState();
_key = GlobalKey();
if (mounted) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
try {
final image = await toBitmap();
setState(() => this.image = image);
} catch (_) {
// do nothing
}
});
}
}
}
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: thicken
description: A widget that creates a thick visual effect by stacking multiple layers of a given child widget with slight translations based on the thickness value.
version: 1.1.2
version: 1.1.3
homepage: https://inidia.app
repository: https://github.com/Nialixus/thicken.git
issue_tracker: https://github.com/Nialixus/thicken/issues
Expand All @@ -11,7 +11,7 @@ screenshots:
path: logo.png
topics:
- utility
- animation
- widget

environment:
sdk: '>=3.4.4 <4.0.0'
Expand Down
114 changes: 97 additions & 17 deletions test/thicken_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,134 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:thicken/thicken.dart';

void main() {
testWidgets('Thicken Widget Test', (WidgetTester tester) async {
debugPrint("✅ Test is started !");
TestWidgetsFlutterBinding.ensureInitialized();

testWidgets('Thicken Widget Initial Test', (WidgetTester tester) async {
debugPrint("\x1B[33m[1] Initial Test is started ... \x1B[32m✔️");
await tester.pumpWidget(
MaterialApp(
const MaterialApp(
home: Thicken(
thickness: 2.5,
child: const Text(
child: Text(
'Thickened Text',
style: TextStyle(fontSize: 24),
),
),
),
);
await tester.pumpAndSettle();
await tester.pump();

// Verify that the text is displayed
expect(
find.text('Thickened Text'),
findsWidgets,
reason: 'Verify that the text is displayed',
);
debugPrint("✅ Thicken child widget found !");
debugPrint("\x1B[33m[2] Verify that the text is displayed ... \x1B[32m✔️");

// // Get the Thicken widget
// Verify that the 'Thicken' widget exists
final thickenWidget = tester.widget<Thicken>(find.byType(Thicken));
expect(
find.byWidget(thickenWidget),
findsOne,
reason: 'Verify that the widget exists',
reason: "Verify that the 'Thicken' widget exists",
);
debugPrint("✅ Thicken widget found !");
debugPrint(
"\x1B[33m[3] Verify that the 'Thicken' widget exists ... \x1B[32m✔️");

// Calculate the total number of layers in the stack
// Verify that the 'Stack' widget exists
final stack = tester.widget<Stack>(find.byType(Stack));
expect(
find.byWidget(stack),
findsOne,
reason: 'Verify that the stack widget exists',
reason: "Verify that the 'Stack' widget exists",
);
debugPrint(
"\x1B[33m[4] Verify that the 'Stack' widget inside 'Thicken' exists ... \x1B[32m✔️",
);
debugPrint("✅ ${stack.children.length} Stack children found !");

await tester.pumpAndSettle();
// Calculate the expected number of layers
final expectedLayers = 3 + (thickenWidget.thickness.floor() * 2);
// Verify that the child of stack widget is only one
expect(
stack.children.length,
expectedLayers * expectedLayers + 1,
reason: 'Verify that the expected number of layers is rendered',
1,
reason: 'Verify that the child of stack widget is only one',
);
debugPrint("✅ $expectedLayers Layers found as expected !");
debugPrint("✅ Test is finished !");
debugPrint(
"\x1B[33m[5] Verify that the child of stack widget is only ${stack.children.length} found ... \x1B[32m✔️",
);
debugPrint("\x1B[33m[5] Initial test is finished ... \x1B[32m✔️");
});

testWidgets('Thicken Widget Final Test', (tester) async {
debugPrint("\x1B[33m[6] Final test is started ... \x1B[32m✔️");
await tester.runAsync(() async {
await tester.pumpWidget(
const MaterialApp(
home: Thicken(
thickness: 2.5,
child: Text(
'Thickened Text',
style: TextStyle(fontSize: 24),
),
),
),
);
await Future.delayed(const Duration(seconds: 1));
await tester.pumpAndSettle();
await tester.pump();

// Verify that the text is displayed
expect(
find.text('Thickened Text'),
findsWidgets,
reason: 'Verify that the text is displayed',
);
debugPrint(
"\x1B[33m[7] Verify that the text is displayed ... \x1B[32m✔️");

// Verify that the 'Thicken' widget exists
final thickenWidget = tester.widget<Thicken>(find.byType(Thicken));
expect(
find.byWidget(thickenWidget),
findsOne,
reason: "Verify that the 'Thicken' widget exists",
);
debugPrint(
"\x1B[33m[8] Verify that the 'Thicken' widget exists ... \x1B[32m✔️");

// Verify that the 'Stack' widget exists
final stack = tester.widget<Stack>(find.byType(Stack));
expect(
find.byWidget(stack),
findsOne,
reason: "Verify that the 'Stack' widget exists",
);
debugPrint(
"\x1B[33m[9] Verify that the 'Stack' widget inside 'Thicken' exists ... \x1B[32m✔️",
);
final calculated = 3 + (thickenWidget.thickness.floor() * 2);
final layers = calculated * calculated + 1;
expect(
stack.children.length,
layers,
reason: 'Verify that the child of stack widget is only one',
);
debugPrint(
"\x1B[33m[10] Verify that the child of stack widget is equal to $layers layers. ... \x1B[32m✔️",
);

// Verify that the 'Image.memory' widget exists
expect(
tester.allWidgets.any((child) => child is Image),
isTrue,
reason: "Verify that the 'Image.memory' widget exists",
);
debugPrint(
"\x1B[33m[11] Verify that the 'Image.memory' widget inside 'Thicken' exists ... \x1B[32m✔️",
);
debugPrint("\x1B[33m[12] Final test is finished ... \x1B[32m✔️");
});
});
}

0 comments on commit 92723d4

Please sign in to comment.