diff --git a/README.md b/README.md index f810cf73..2c39d769 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ + [![Pub][pub_badge]][pub] [![BuyMeACoffee][buy_me_a_coffee_badge]][buy_me_a_coffee] [][flutter_favorite] **Slidable is a [Flutter Favorite][flutter_favorite] package!** - # flutter_slidable A Flutter implementation of slidable list item with directional slide actions that can be dismissed. @@ -12,12 +12,12 @@ A Flutter implementation of slidable list item with directional slide actions th Our top sponsors are shown below! [[Become a Sponsor](https://github.com/sponsors/letsar)] - +
+

Try the Flutter Chat Tutorial  💬 -
@@ -28,16 +28,16 @@ You can read this small guide to migrate from the 0.6 to the 1.0 version: https: ## Features -* Accepts start (left/top) and end (right/bottom) action panes. -* Can be dismissed. -* 4 built-in action panes. -* 2 built-in slide action widgets. -* 1 built-in dismiss animation. -* You can easily create custom layouts and animations. -* You can use a builder to create your slide actions if you want special effects during animation. -* Closes when a slide action has been tapped (overridable). -* Closes when the nearest `Scrollable` starts to scroll (overridable). -* Option to disable the slide effect easily. +- Accepts start (left/top) and end (right/bottom) action panes. +- Can be dismissed. +- 4 built-in action panes. +- 2 built-in slide action widgets. +- 1 built-in dismiss animation. +- You can easily create custom layouts and animations. +- You can use a builder to create your slide actions if you want special effects during animation. +- Closes when a slide action has been tapped (overridable). +- Closes when the nearest `Scrollable` starts to scroll (overridable). +- Option to disable the slide effect easily. ## Install @@ -78,14 +78,14 @@ Slidable( onPressed: doNothing, backgroundColor: Color(0xFFFE4A49), foregroundColor: Colors.white, - icon: Icons.delete, + icon: const Icon(Icons.delete), label: 'Delete', ), SlidableAction( onPressed: doNothing, backgroundColor: Color(0xFF21B7CA), foregroundColor: Colors.white, - icon: Icons.share, + icon: const Icon(Icons.share), label: 'Share', ), ], @@ -101,14 +101,14 @@ Slidable( onPressed: doNothing, backgroundColor: Color(0xFF7BC043), foregroundColor: Colors.white, - icon: Icons.archive, + icon: const Icon(Icons.archive), label: 'Archive', ), SlidableAction( onPressed: doNothing, backgroundColor: Color(0xFF0392CF), foregroundColor: Colors.white, - icon: Icons.save, + icon: const Icon(Icons.save), label: 'Save', ), ], @@ -188,10 +188,11 @@ I'm working on my packages on my free-time, but I don't have as much time as I w Feel free to contribute to this project. -If you find a bug or want a feature, but don't know how to fix/implement it, please fill an [issue][issue]. +If you find a bug or want a feature, but don't know how to fix/implement it, please fill an [issue][issue]. If you fixed a bug or implemented a feature, please send a [pull request][pr]. + [github_action_badge]: https://github.com/letsar/flutter_slidable/workflows/Build/badge.svg [github_action]: https://github.com/letsar/flutter_slidable/actions [pub_badge]: https://img.shields.io/pub/v/flutter_slidable.svg diff --git a/example/lib/main.dart b/example/lib/main.dart index 4f0f48db..774f1d27 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -41,14 +41,14 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { onPressed: doNothing, backgroundColor: Color(0xFFFE4A49), foregroundColor: Colors.white, - icon: Icons.delete, + icon: Icon(Icons.delete), label: 'Delete', ), SlidableAction( onPressed: doNothing, backgroundColor: Color(0xFF21B7CA), foregroundColor: Colors.white, - icon: Icons.share, + icon: Icon(Icons.share), label: 'Share', ), ], @@ -64,14 +64,14 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { onPressed: (_) => controller.openEndActionPane(), backgroundColor: const Color(0xFF7BC043), foregroundColor: Colors.white, - icon: Icons.archive, + icon: Icon(Icons.archive), label: 'Archive', ), SlidableAction( onPressed: (_) => controller.close(), backgroundColor: const Color(0xFF0392CF), foregroundColor: Colors.white, - icon: Icons.save, + icon: Icon(Icons.save), label: 'Save', ), ], @@ -98,14 +98,14 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { onPressed: doNothing, backgroundColor: Color(0xFFFE4A49), foregroundColor: Colors.white, - icon: Icons.delete, + icon: Icon(Icons.delete), label: 'Delete', ), SlidableAction( onPressed: doNothing, backgroundColor: Color(0xFF21B7CA), foregroundColor: Colors.white, - icon: Icons.share, + icon: Icon(Icons.share), label: 'Share', ), ], @@ -122,14 +122,14 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { onPressed: doNothing, backgroundColor: Color(0xFF7BC043), foregroundColor: Colors.white, - icon: Icons.archive, + icon: Icon(Icons.archive), label: 'Archive', ), SlidableAction( onPressed: doNothing, backgroundColor: Color(0xFF0392CF), foregroundColor: Colors.white, - icon: Icons.save, + icon: Icon(Icons.save), label: 'Save', ), ], diff --git a/example/lib/main_demo.dart b/example/lib/main_demo.dart index afb1b97d..825799fb 100644 --- a/example/lib/main_demo.dart +++ b/example/lib/main_demo.dart @@ -269,7 +269,7 @@ class SlideAction extends StatelessWidget { onPressed: (_) { print(icon); }, - icon: icon, + icon: Icon(icon), label: 'hello', ); } @@ -291,7 +291,7 @@ class Tile extends StatelessWidget { return ActionTypeListener( child: GestureDetector( onTap: () { - print('$text'); + print(text); }, onLongPress: () => Slidable.of(context)!.openEndActionPane(), child: Container( diff --git a/example/lib/main_example.dart b/example/lib/main_example.dart index c782adf3..4c690047 100644 --- a/example/lib/main_example.dart +++ b/example/lib/main_example.dart @@ -218,7 +218,7 @@ class SlideAction extends StatelessWidget { backgroundColor: color, foregroundColor: Colors.white, onPressed: (_) {}, - icon: icon, + icon: Icon(icon), label: label, ); } diff --git a/example/lib/main_outside_list.dart b/example/lib/main_outside_list.dart index cc5ede90..77e36242 100644 --- a/example/lib/main_outside_list.dart +++ b/example/lib/main_outside_list.dart @@ -202,7 +202,7 @@ class SlideAction extends StatelessWidget { backgroundColor: color, foregroundColor: Colors.white, onPressed: (_) {}, - icon: icon, + icon: Icon(icon), label: 'hello', ); } diff --git a/lib/src/actions.dart b/lib/src/actions.dart index 418a9fb8..eb49ebd0 100644 --- a/lib/src/actions.dart +++ b/lib/src/actions.dart @@ -85,11 +85,8 @@ class CustomSlidableAction extends StatelessWidget { @override Widget build(BuildContext context) { - final effectiveForegroundColor = foregroundColor ?? - (ThemeData.estimateBrightnessForColor(backgroundColor) == - Brightness.light - ? Colors.black - : Colors.white); + final effectiveForegroundColor = + foregroundColor ?? (ThemeData.estimateBrightnessForColor(backgroundColor) == Brightness.light ? Colors.black : Colors.white); return Expanded( flex: flex, @@ -142,6 +139,7 @@ class SlidableAction extends StatelessWidget { this.label, this.borderRadius = BorderRadius.zero, this.padding, + this.labelStyle, }) : assert(flex > 0), assert(icon != null || label != null), super(key: key); @@ -162,7 +160,7 @@ class SlidableAction extends StatelessWidget { final SlidableActionCallback? onPressed; /// An icon to display above the [label]. - final IconData? icon; + final Widget? icon; /// The space between [icon] and [label] if both set. /// @@ -178,13 +176,16 @@ class SlidableAction extends StatelessWidget { /// Padding of the OutlinedButton final EdgeInsets? padding; + /// The style to use for the [label]. + final TextStyle? labelStyle; + @override Widget build(BuildContext context) { final children = []; if (icon != null) { children.add( - Icon(icon), + icon!, ); } @@ -199,6 +200,7 @@ class SlidableAction extends StatelessWidget { Text( label!, overflow: TextOverflow.ellipsis, + style: labelStyle, ), ); } diff --git a/test/actions_test.dart b/test/actions_test.dart index 745a17a3..4cb84e83 100644 --- a/test/actions_test.dart +++ b/test/actions_test.dart @@ -80,7 +80,7 @@ void main() { children: [ SlidableAction( onPressed: (_) => logs.add('pressed'), - icon: Icons.ac_unit, + icon: const Icon(Icons.ac_unit), ) ], ), @@ -99,7 +99,7 @@ void main() { children: [ SlidableAction( onPressed: (_) => logs.add('pressed'), - icon: Icons.ac_unit, + icon: const Icon(Icons.ac_unit), label: 'my_label', ) ], diff --git a/test/dismissible_pane_test.dart b/test/dismissible_pane_test.dart index c63f7a20..1cc29670 100644 --- a/test/dismissible_pane_test.dart +++ b/test/dismissible_pane_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/src/action_pane_motions.dart'; import 'package:flutter_slidable/src/actions.dart'; @@ -34,8 +33,8 @@ void main() { ), motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: const SizedBox.expand(), @@ -77,8 +76,8 @@ void main() { ), motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: const SizedBox.expand(), @@ -124,8 +123,8 @@ void main() { ), motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: const SizedBox.expand(), @@ -149,9 +148,7 @@ void main() { expect(dismissed, isTrue); }); - testWidgets( - 'startActionPane cannot be drag dismissed if dragDismissible is false', - (tester) async { + testWidgets('startActionPane cannot be drag dismissed if dragDismissible is false', (tester) async { bool dismissed = false; void handleDismissed() { dismissed = true; @@ -174,8 +171,8 @@ void main() { ), motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: const SizedBox.expand(), @@ -199,9 +196,7 @@ void main() { expect(dismissed, isFalse); }); - testWidgets( - 'when the drag is not past the dismissThreshold, the Slidable stays open', - (tester) async { + testWidgets('when the drag is not past the dismissThreshold, the Slidable stays open', (tester) async { bool dismissed = false; void handleDismissed() { dismissed = true; @@ -226,8 +221,8 @@ void main() { ), motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: const SizedBox.expand(), @@ -282,8 +277,8 @@ void main() { ), motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: const SizedBox.expand(), @@ -340,8 +335,8 @@ void main() { ), motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: Builder( diff --git a/test/slidable_test.dart b/test/slidable_test.dart index f2a5a477..48c7523e 100644 --- a/test/slidable_test.dart +++ b/test/slidable_test.dart @@ -1,4 +1,3 @@ -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/src/action_pane_motions.dart'; import 'package:flutter_slidable/src/actions.dart'; @@ -7,9 +6,7 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('Slidable', () { - testWidgets( - 'child should be able to open the horitzontal start action pane', - (tester) async { + testWidgets('child should be able to open the horitzontal start action pane', (tester) async { const gestureDetectorKey = ValueKey('gesture_detector'); const startActionPaneKey = ValueKey('start'); const endActionPaneKey = ValueKey('end'); @@ -21,16 +18,16 @@ void main() { key: startActionPaneKey, motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), endActionPane: ActionPane( key: endActionPaneKey, motion: const ScrollMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: Builder(builder: (context) { @@ -55,8 +52,7 @@ void main() { expect(find.byKey(endActionPaneKey), findsNothing); }); - testWidgets('child should be able to open the horizontal end action pane', - (tester) async { + testWidgets('child should be able to open the horizontal end action pane', (tester) async { const gestureDetectorKey = ValueKey('gesture_detector'); const startActionPaneKey = ValueKey('start'); const endActionPaneKey = ValueKey('end'); @@ -68,16 +64,16 @@ void main() { key: startActionPaneKey, motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), endActionPane: ActionPane( key: endActionPaneKey, motion: const ScrollMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: Builder(builder: (context) { @@ -102,8 +98,7 @@ void main() { expect(find.byKey(endActionPaneKey), findsOneWidget); }); - testWidgets('child should be able to open the vertical start action pane', - (tester) async { + testWidgets('child should be able to open the vertical start action pane', (tester) async { const gestureDetectorKey = ValueKey('gesture_detector'); const startActionPaneKey = ValueKey('start'); const endActionPaneKey = ValueKey('end'); @@ -116,16 +111,16 @@ void main() { key: startActionPaneKey, motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), endActionPane: ActionPane( key: endActionPaneKey, motion: const ScrollMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: Builder(builder: (context) { @@ -150,8 +145,7 @@ void main() { expect(find.byKey(endActionPaneKey), findsNothing); }); - testWidgets('child should be able to open the vertical end action pane', - (tester) async { + testWidgets('child should be able to open the vertical end action pane', (tester) async { const gestureDetectorKey = ValueKey('gesture_detector'); const startActionPaneKey = ValueKey('start'); const endActionPaneKey = ValueKey('end'); @@ -164,16 +158,16 @@ void main() { key: startActionPaneKey, motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), endActionPane: ActionPane( key: endActionPaneKey, motion: const ScrollMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: Builder(builder: (context) { @@ -213,8 +207,8 @@ void main() { key: endActionPaneKey, motion: const ScrollMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: Builder( @@ -253,8 +247,8 @@ void main() { key: startActionPaneKey, motion: const ScrollMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ), child: Builder( @@ -279,17 +273,15 @@ void main() { expect(tester.getTopLeft(find.byKey(childKey)), const Offset(0, 0)); }); - testWidgets( - 'should work if TextDirection.rtl and only startActionPane is set', - (tester) async { + testWidgets('should work if TextDirection.rtl and only startActionPane is set', (tester) async { const gestureDetectorKey = ValueKey('gesture_detector'); const actionPaneKey = ValueKey('action_pane'); final actionPane = ActionPane( key: actionPaneKey, motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], ); @@ -318,16 +310,15 @@ void main() { expect(find.byKey(actionPaneKey), findsOneWidget); }); - testWidgets('should work if TextDirection.rtl and only endActionPane is set', - (tester) async { + testWidgets('should work if TextDirection.rtl and only endActionPane is set', (tester) async { const gestureDetectorKey = ValueKey('gesture_detector'); const actionPaneKey = ValueKey('action_pane'); final actionPane = ActionPane( key: actionPaneKey, motion: const BehindMotion(), children: [ - SlidableAction(onPressed: (_) {}, icon: Icons.share), - SlidableAction(onPressed: (_) {}, icon: Icons.delete), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.share)), + SlidableAction(onPressed: (_) {}, icon: const Icon(Icons.delete)), ], );