Software Development Executive - II
Last updated on Nov 11, 2024
Last updated on Nov 11, 2024
From a simple tap to a complex pinch-to-zoom, gestures transform ordinary apps into captivating experiences. For Flutter developers, mastering these gestures is essential, as Google’s open-source UI toolkit allows the creation of visually stunning, natively compiled apps for mobile, web, and desktop—all from one versatile codebase.
This blog will explore Flutter’s robust gesture system, from fundamental actions like taps to advanced gestures like scaling and dragging. Packed with practical examples and actionable insights, you’ll gain a comprehensive toolkit to enrich your app’s user interface and make interactions feel responsive and natural.
Flutter's gesture system, wrapped in a non-visual widget known as the GestureDetector class, handles touches on a screen. The gesture detector class identifies gestures, triggering corresponding events when user makes contact with the screen. It's like a Swiss Army Knife for gesture detection in your Flutter application.
At its core, the Flutter gesture system reacts to user inputs by triggering callback functions. The GestureDetector widget in Flutter is designed to detect gestures such as tap, double tap, long press, vertical drag, and horizontal drag, among others.
Let's understand a little about the GestureDetector Widget:
A GestureDetector widget doesn't have a visual representation but instead, assigns recognition to the gestures of its child widgets. That's how a tap on a FlatButton, a Card, or any widget at all, can trigger an innovative interaction in your app for creating applications that users love.
For example, a simple GestureDetector widget detecting a tap gesture might look like this:
1 return GestureDetector( 2 onTap: () { 3 // action on tap 4 }, 5 child: FlutterLogo(size: 200.0), 6 ); 7
The GestureDetector widget is the primary workhorse to handle taps, double taps, long presses, and other gestures. This non-visual widget serves as the first point of contact when the user touches the screen.
In essence, it acts like a transparent controller. While it does not provide any buildContext on the screen, it does recognize gestures and calls the corresponding gesture handler in its non-null callbacks.
The GestureDetector widget and Flutter's gesture mechanism are closely related concepts that go hand-in-hand. Let's continue by delving deeper into the different types of gesture detectors provided by Flutter.
The GestureDetector's callback functions are associated with specific types of user interaction. Let's investigate some of the most popular ones, namely onTap, onDoubleTap, and onLongPress.
The onTap callback is triggered when a user tapped the GestureDetector widget. This is the most basic form of interaction, equivalent to a mouse click in desktop applications. Usually, onTap is used to move between pages, update state, or trigger animations.
An example of using onTap function in a Flutter application to navigate to a new page might look like this:
1 return GestureDetector( 2 onTap: () { 3 Navigator.push( 4 context, 5 MaterialPageRoute(builder: (context) => SecondPage()), 6 ); 7 }, 8 child: Text('Go to Second Page!'), 9 ); 10
The onDoubleTap callback is triggered when a user tapped twice in quick succession on the GestureDetector widget, or essentially, a double tap. Common use cases for this gesture include zooming in or out on an image and playing or pausing media.
Here's a Flutter GestureDetector example illustrating the use of onDoubleTap gesture to toggle the size of a FlutterLogo widget:
1 class MyApp extends StatelessWidget { 2 double sideLength = 50; 3 4 void toggleSize() { 5 if (sideLength == 50) { 6 sideLength = 100; 7 } else { 8 sideLength = 50; 9 } 10 } 11 12 @override 13 Widget build(BuildContext context) { 14 return MaterialApp( 15 home: Scaffold( 16 appBar: AppBar(title: Text('Flutter GestureDetector Example')), 17 body: Center( 18 child: GestureDetector( 19 onDoubleTap: toggleSize, 20 child: AnimatedContainer( 21 width: sideLength, 22 height: sideLength, 23 color: Colors.amber, 24 duration: Duration(seconds: 2), 25 ), 26 ), 27 ), 28 ), 29 ); 30 } 31 } 32 33 void main() { 34 runApp(MyApp()); 35 } 36
The onLongPress callback triggers when a user touches the screen for a long period, typically around one second. This gesture often uncovers secondary functionality; for example, it might open a menu or allow elements to be moved or edited.
An example of using the onLongPress callback to present a dialog might look like this:
1 return GestureDetector( 2 onLongPress: () { 3 showDialog<String>( 4 context: context, 5 builder: (BuildContext context) => AlertDialog( 6 title: const Text('Long Press Detected'), 7 content: const Text('You have long-pressed the widget.'), 8 actions: <Widget>[ 9 TextButton( 10 onPressed: () => Navigator.pop(context, 'OK'), 11 child: const Text('OK'), 12 ), 13 ], 14 ), 15 ); 16 }, 17 child: Container( 18 color: Colors.amber, 19 height: 100.0, 20 width: 100.0, 21 alignment: Alignment.center, 22 child: const Text('Long Press Me'), 23 ), 24 ); 25
In addition to providing recognition of various gestures, the GestureDetector widget is also a supreme tool for decorating and controlling user interaction. Let's discuss how we can implement GestureDetector within our UI, provide visual feedback, and handle complex gesture interactions.
The GestureDetector widget needs to be strategically located in your widget tree to ensure the right widgets respond to user interactions. Generally, it should wrap the specific widget (or widgets) you want to attach the gesture handler to.
Apart from triggering actions, gestures also have the crucial role of giving users feedback on their interaction. For instance, a button may change color slightly when the user taps on it, or an item might highlight when long-pressed.
Flutter's Gesture system includes the "gesture arena" concept to resolve conflicts that occur when multiple gestures compete against one another to recognize the same sequence of user input. This approach provides a way to implement complex interactions by effectively managing the gesture arena.
For instance, when the user makes contact with the screen, each gesture recognizer joins in the gesture arena, and when the Flutter framework determines which gesture won, it cancels the others.
Let's proceed to the next chapter, which focuses on adding practical interaction features to your Flutter applications.
To add more interactions, let's dive into the dynamic world of Flutter swipe gesture.
Swipe gesture, an essential part of touch-based devices often triggers navigation or action in mobile apps. In Flutter, swipe gestures are managed through a horizontal drag or vertical drag.
A swipe event in Flutter comprises three states: beginning (onDragStart in Flutter), moving (the onDragUpdate callback in Flutter), and ending (the onDragEnd callback).
Moving a UI element on user touch involves listening to the onPanUpdate callback, which tracks the position of the user's finger and changes the position of the widget in the same location.
In Flutter, you can move a UI element by using either the GestureDetector's onPanUpdate() event or the Draggable widget. Here's an example of how you would create a Draggable widget:
1 class MyApp extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 return MaterialApp( 5 home: Scaffold( 6 appBar: AppBar(title: Text('Flutter Draggable Example')), 7 body: Stack( 8 children: <Widget>[ 9 Draggable( 10 child: Icon(Icons.portrait, size: 50.0), 11 feedback: Icon(Icons.portrait, size: 50.0), 12 childWhenDragging: Icon(Icons.portrait, size: 50.0, color: Colors.grey), 13 ), 14 ], 15 ), 16 ), 17 ); 18 } 19 } 20 21 void main() { 22 runApp(MyApp()); 23 } 24
In this example, moving the portrait icon around the screen is as simple as dragging/selecting it and then moving it to the desired location.
Swiping can convey various meanings depending on the application's context, such as the popular ‘Swipe to Dismiss’ functionality seen in many modern apps.
The Dismissible widget in Flutter implements the Swipe to Dismiss pattern. It requires an unique key to distinguish between different Dismissible widgets.
Here's an example:
1 class MyApp extends StatefulWidget { 2 _MyAppState createState() => _MyAppState(); 3 } 4 5 class _MyAppState extends State<MyApp> { 6 final items = List<String>.generate(20, (i) => "Item ${i + 1}"); 7 8 @override 9 Widget build(BuildContext context) { 10 return MaterialApp( 11 home: Scaffold( 12 appBar: AppBar( 13 title: Text('Swipe to Dismiss Example'), 14 ), 15 body: ListView.builder( 16 itemCount: items.length, 17 itemBuilder: (context, index) { 18 return Dismissible( 19 key: Key(items[index]), 20 onDismissed: (direction) { 21 setState(() { 22 items.removeAt(index); 23 }); 24 Scaffold.of(context).showSnackBar(SnackBar(content: Text("Item dismissed"))); 25 }, 26 background: Container(color: Colors.red), 27 child: ListTile(title: Text('${items[index]}')), 28 ); 29 }, 30 ), 31 ), 32 ); 33 } 34 } 35 36 void main() { 37 runApp(MyApp()); 38 } 39
Flutter respects the user's platform's conventions and interaction patterns. As a part of this, Flutter provides out-of-the-box support for the 'back swipe' navigation gesture on iOS.
Adding material touch ripples can significantly enhance how interactive your Flutter application feels.
Flutter's affiliation with Material Design principles runs deep, providing developers with a host of ready-to-use widgets to create visually stunning apps. A 'ripple effect' is one such interaction that Material Design proposes when a user taps a button or a list item, providing immediate visual feedback.
Material touch ripples are essentially a feedback mechanism. They reassure users that they've made a successful tap, providing them with a short visual response that mimics a ripple effect. Typically, this 'ripple' radiates from the user's point of touch on the screen, adding a dynamic aspect.
To implement ripples, Flutter provides the InkWell and InkResponse widgets. When the user taps the screen, these widgets not only create the ripple effect but can also call onTap or any other GestureDetector callbacks.
Here's an example of how to add touch ripples using the InkWell widget:
1 InkWell( 2 onTap: () { 3 print('InkWell tapped'); 4 }, 5 child: Container( 6 width: 100.0, 7 height: 100.0, 8 decoration: BoxDecoration( 9 color: Colors.blue, 10 ), 11 child: Center( 12 child: Text('Tap Me'), 13 ), 14 ), 15 ) 16
Summarizing our journey exploring Flutter gestures, we've unlocked the potential of handling user interactions and offering visual feedback, significantly enhancing the user experience of our Flutter applications.
We've delved into Flutter's GestureDetector widget, examining various callback functions like onTap, onDoubleTap, and onLongPress. Further, we've understood how to drag and move a UI element, implement swipe dismiss, and utilized the Flutter iOS back gesture.
As with any learning, getting comfortable with Flutter gestures requires practice. So, let your creativity out and experiment with all the knowledge gained from the blog. This exploration will pave the way to making more interactive and user-friendly applications with Flutter.
And that's it! We've come to the end of our journey through Flutter Gestures. I hope you've found this helpful and enjoyable.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.
Tired coding all day?
Do it with a few clicks.