Search code examples
flutterformsdartuser-interface

Making a multi page form in flutter


so I want to make a multipage form that has a linear progress indicator at the top instead of the normal stepper widget one, I've tried SO many different websites and nothing is both working and has everything I need,

what I want is:

  • to have a different page for each step
  • a way to return the data collected from each step
  • a bar at the top to show how close you are to finishing the form
  • and a way to edit each page serperately

any advice on how I'd do this would be so so so appreciated and thank you in advance


Solution

  • at first, it's a shorthand solution (fast solution) for your problem.

    the below code should be refactored and and can be enhanced, but i'm presenting an idea. here's the code, the coming is the driver class that manages the Linear Progress Indicator(LPI) and page view.

    class HomeScreen extends StatefulWidget {
       HomeScreen({super.key});
    
      @override
      State<HomeScreen> createState() => _HomeScreenState();
    }
    
    class _HomeScreenState extends State<HomeScreen> {
      var pages =  [ FirstPage(),SecondPage(), ThirdPage(),FourthPage(),];
    
      int currentIndex = 0;
    
      @override
      Widget build(BuildContext context) {
        print('called');
        return SafeArea(
          child: Scaffold(
            body: Column(
              children: [
                 Padding(
                   padding: EdgeInsets.symmetric(horizontal: 10,vertical: 30),
                   child: LinearProgressIndicator(
                     color: Colors.blue.shade700,
                     value: (currentIndex+1) /4,
                     borderRadius: BorderRadius.circular(10),
                     minHeight: 20,
                   ),
                 ),
                Expanded(
                  child: Padding(
                    padding: const EdgeInsets.all(15),
                    child: PageView(
                      children: pages,
                      onPageChanged: (i){
                        setState(() {
                          currentIndex = i;
                        });
                      },
                      padEnds: true,
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    ok, pages variable is a list of widgets, in which every widget holds the presented content in every page. it's a page view

    look at FirstPage Widget implementation

    class FirstPage extends StatelessWidget {
    
      static var controller = TextEditingController();
      const FirstPage({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text('First Page'),
            ),
            TextFormField(
              controller: controller,
              decoration: InputDecoration(
                  label: Text('Name'),
                enabledBorder: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(8),
                  borderSide: BorderSide(width: 1,color: Colors.blue)
                )
              ),
            ),
          ],
        );
      }
    }
    

    here's second page, regardless the number of pages you have. the third page will have the same implementation like the others.

    class SecondPage extends StatelessWidget {
      static var controller = TextEditingController();
      const SecondPage({super.key});
    
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text('Second Page'),
            ),
            TextFormField(
              controller: controller,
              decoration: InputDecoration(
                label: Text('phone'),
                  enabledBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide: BorderSide(width: 1,color: Colors.blue)
                  )
              ),
            ),
          ],
        );
      }
    }
    

    if you notice every page (class) has a controller for its text field which enables us to get the written text at last.

    the last page of page view

    class FourthPage extends StatelessWidget {
      static var controller = TextEditingController();
      const FourthPage({super.key});
    
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Text('Fourth Page'),
            ),
            TextFormField(
              controller: controller,
              onFieldSubmitted: (s){
                Navigator.of(context).push(
                    MaterialPageRoute(
                        builder: (context)=> FinalScreen(),
                        settings: RouteSettings(
                          arguments: {
                            'first' : FirstPage.controller.text,
                            'second' : SecondPage.controller.text,
                            'third' : ThirdPage.controller.text,
                            'fourth' : FourthPage.controller.text
                          },
                        )
                    )
                );
              },
              decoration: InputDecoration(
                  label: Text('Password'),
                  enabledBorder: OutlineInputBorder(
                      borderRadius: BorderRadius.circular(8),
                      borderSide: BorderSide(width: 1,color: Colors.blue)
                  )
              ),
            ),
          ],
        );
      }
    }
    

    the fourth and last page has a critical lines of code that are executed when user submits the value to text field( it navigate to the home or final page and send data to that page using route settings ).

    here's the final page which presents user gathered data

    class FinalScreen extends StatelessWidget {
      const FinalScreen({super.key});
    
      @override
      Widget build(BuildContext context) {
    
        var data = ModalRoute.of(context)?.settings.arguments as Map<String,String>;
    
        return Scaffold(
          appBar: AppBar(
            title: Text('Final Screen'),
            centerTitle: true,
          ),
          body: Container(
            width: double.infinity,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Text('first page data: ${data['first']}'),
                Text('second page data: ${data['second']}'),
                Text('third page data: ${data['third']}'),
                Text('fourth page data: ${data['fourth']}'),
              ],
            ),
          ),
        );
      }
    }
    

    notice:

    • the above code doesn't make text input validation, you should do (wrap them with form you know) and validate user input on each page view swipe. if the input violates your rules swipe back to the previous page and alert the user (use page view controller).
    • thirdPage isn't provided because it has the same implementation just like the first and second.

    here's the output: enter image description here

    hope it helps you.