Reputation: 6006
I'm creating a registration form in Flutter, and I would like the user to go through steps. Every step should transition to the next step with a sliding effect. For example, if I am on Step 1, moving to Step 2 should slide the form to the left, and I should get Form 2. Then if I go back to form 1, it should slide the form to the right.
I tried to do that with multiple routes:
routes: {
'/': (context) => HomePage(),
'/step1': (context) => FormStep1(),
'/step2': (context) => FormStep2(),
},
Then on submit:
Navigator.push(
context,
EnterExitRoute(exitPage: FormStep1(), enterPage: FormStep2())
);
But that makes the App Bar slide as well, and I want only the form to slide.
Upvotes: 8
Views: 9838
Reputation: 6006
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:
WillPopScope
so "back" button will
affect navigation.physics: NeverScrollableScrollPhysics()
option on the PageView
builder so it will not be affected by a swipe gesture. _nextFormStep()
method, which moves to the next form.WillPopScope()
in the list is simply the form / widget you want to be slided.Upvotes: 13
Reputation:
as an option you can wrap pages with Navigator widget something 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
Upvotes: 1