Open In App

Flow Widget in Flutter

Last Updated : 24 Jan, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

The Flow widget in Flutter is a layout widget that positions children’s elements in a flow along with sizing and positions its children proficiently using FlowDelegate. It allows you to create a grid-like layout where the children are positioned according to a given alignment, and they flow from one row to the next.

In this article, we are going to create a layout containing a floating menu that can expand/collapse using the Flow widget, like the video below.

Constructor:

Flow({
    Key? key,
    required this.delegate,
    List<Widget> children = const <Widget>[],
})

To make an example of Flow, the only required contention is delegate. You need to pass a FlowDelegate which is capable to control the presence of the flow design.

Step By Step Implementation

Step 1: Create a New Project in Android Studio

To set up Flutter Development on Android Studio please refer to Android Studio Setup for Flutter Development, and then create a new project in Android Studio please refer to Creating a Simple Application in Flutter.

Step 2: Add SingleTickerProviderStateMixin

It becomes conceivable to get the TickerProvider which is required when calling the constructor of AnimationController.

 

Step 3: Create lastIconClicked data

This helps us indicate the previous icon that was clicked. Let’s see the video of how it works

 

Explanation:  We can see when I click the menu button the notification icon was selected and now if we click the setting icon button the setting button is indicated as the last selected icon.

Step 4: Create a list of menu items

This contains all the icons in the list of menu items

 

Step 5:  Create initState method

Create menuAnimation which is equal to Animation Controller(). Set duration to 250 milliseconds.

 

Step 6: Create a Delegate

Inside Flow Widget we create a delegate which has FlowMenuDelegate and inside that, we have passed menu animation

 

Step 7: Create a delegate for Flow Menu which extends Flow Delegate

Inside pass an argument that creates menu animation. Now create 2 overrides shouldRepaint and paintChildren. Inside paintChildren the logic will be how the animation will be displayed

 

Step 8: Completing the Flow Widget

  • Adding children this will come from menu items which is the list of icons.
  • Then we will use .map and .toList() which will convert to a list of widgets.
  • Inside this, we will put padding and the child will be floatingActionButton because each icon you see is a floating action button.
  • Giving backgroundColor will depend on lastIconClicked or not.
  • Now add a color property to splashColor.
  • Inside onPressed we have put logic that only the last icon clicked should be orange.
  • Verify the menu animation status if AnimationStatus is completed then reverse the animation else moveForward. This will trigger the animation forward or reverse.
  • The child of the floating action button will be an icon coming from a list of icons.

 

Complete Code: 

Dart




import 'package:flutter/material.dart';
  
void main() {
    runApp(const MyApp());
}
  
class MyApp extends StatelessWidget {
    const MyApp({Key ? key}): super(key: key);
  
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            title: 'Flutter Demo',
            theme: ThemeData(
                primarySwatch: Colors.green,
            ),
            home: const MyHomePage(),
        );
    }
}
  
class MyHomePage extends StatefulWidget {
    const MyHomePage({Key ? key}): super(key: key);
  
    @override
    State <MyHomePage> createState() => _MyHomePageState();
}
  
class _MyHomePageState extends State <MyHomePage> with SingleTickerProviderStateMixin {
    late AnimationController menuAnimation;
    IconData lastIconClicked = Icons.notifications;
    final List <IconData> menuItems = <IconData> [
        Icons.home,
        Icons.new_releases,
        Icons.notifications,
        Icons.settings,
        Icons.menu,
    ];
  
    @override
    void initState() {
        super.initState();
        menuAnimation = AnimationController(
        duration: const Duration(milliseconds: 250), vsync: this);
    }
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
          appBar: AppBar(
              title: Text('GeeksforGeeks'),
          ),
          // Flow Widget
          body: Flow(
              delegate: FlowMenuDelegate(menuAnimation: menuAnimation),
              children: menuItems.map <Widget> ((IconData icon) => Padding(
                  padding: const EdgeInsets.all(5.0),
                  child: FloatingActionButton(
                      backgroundColor: lastIconClicked == icon ? Colors.orangeAccent : Colors.grey,
                      splashColor: Colors.orangeAccent,
                      onPressed: () {
                          if (icon != Icons.menu) {
                              setState(() {
                                  lastIconClicked = icon;
                              });
                          }
                          menuAnimation.status == AnimationStatus.completed ? menuAnimation.reverse() : menuAnimation.forward();
                      },
                      child: Icon(icon),
                  ),
              )).toList(),
          ),
      );
  }
}
  
// flowMenuDelegate class that extends FlowDelegate
class FlowMenuDelegate extends FlowDelegate {
    FlowMenuDelegate({ required this.menuAnimation}): super(repaint: menuAnimation);
  
    final Animation <double>  menuAnimation;
    
    // shouldRepaint override
    @override
    bool shouldRepaint(FlowMenuDelegate oldDelegate) {
        return menuAnimation != oldDelegate.menuAnimation;
    }
    
    // paintChildren override
    @override
    void paintChildren(FlowPaintingContext context) {
        double dx = 0.0;
        for (int i = 0; i < context.childCount; ++i) {
            dx = context.getChildSize(i)!.width * i;
            context.paintChild(
                i,
                transform: Matrix4.translationValues(dx * menuAnimation.value, 0, 0),
            );
        }
    }
}


Output:



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads