Sliding form steps in Flutter?
With an advice from a friend, I ended up using PageView
. That way I didn't have to make a new route for every step.
class _RegisterFormState extends State<RegisterForm> { final _formsPageViewController = PageController(); List _forms; @override Widget build(BuildContext context) { _forms = [ WillPopScope( onWillPop: () => Future.sync(this.onWillPop), child: Step1Container(), ), WillPopScope( onWillPop: () => Future.sync(this.onWillPop), child: Step2Container(), ), ]; return Expanded( child: PageView.builder( controller: _formsPageViewController, physics: NeverScrollableScrollPhysics(), itemBuilder: (BuildContext context, int index) { return _forms[index]; }, ), ); } void _nextFormStep() { _formsPageViewController.nextPage( duration: Duration(milliseconds: 300), curve: Curves.ease, ); } bool onWillPop() { if (_formsPageViewController.page.round() == _formsPageViewController.initialPage) return true; _formsPageViewController.previousPage( duration: Duration(milliseconds: 300), curve: Curves.ease, ); return false; }}
Explanation:
- I'm wrapping every form with
WillPopScope
so "back" button willaffect navigation. - I'm using
physics: NeverScrollableScrollPhysics()
option on thePageView
builder so it will not be affected by a swipe gesture. - On each button of a form step (except last step) I call the
_nextFormStep()
method, which moves to the next form. - The child of each
WillPopScope()
in the list is simply the form / widget you want to be slided.
as an option you can wrap pages with Navigator widgetsomething like this
import 'package:flutter/cupertino.dart';import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('Title')), body: SafeArea( child: WillPopScope( onWillPop: () async => !await _navigatorKey.currentState.maybePop(), child: Navigator( key: _navigatorKey, onGenerateRoute: (settings) { switch (settings.name) { case '/': return MaterialPageRoute(builder: (context) => HomePage()); break; case '/step1': return CupertinoPageRoute(builder: (context) => FormStep1()); break; case '/step2': return CupertinoPageRoute(builder: (context) => FormStep2()); break; } }, ), ), ), ), ); }}class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Container( color: Colors.green[200], child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('HomePage'), RaisedButton( onPressed: () => Navigator.pushNamed(context, '/step1'), child: Text('Start'), ), ], ), ); }}class FormStep1 extends StatelessWidget { @override Widget build(BuildContext context) { return Container( color: Colors.blue[200], child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('FormStep1'), RaisedButton( onPressed: () => Navigator.pushNamed(context, '/step2'), child: Text('Next'), ), ], ), ); }}class FormStep2 extends StatelessWidget { @override Widget build(BuildContext context) { return Container( color: Colors.yellow[200], child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('FormStep2'), RaisedButton(onPressed: () {}, child: Text('Next')), ], ), ); }}
also instead of CupertinoPageRoute you can use any custom Route with any transition