Reputation: 266
I currently have a little problem in Flutter and can't get any further.
I would like to create a PageView process in which the user enters data on the various pages.
This is what my PageView currently looks like:
class AddNews extends StatelessWidget {
final controller = PageController(initialPage: 0);
final List<Widget> _addPages = [SelectType(), AddInformation()];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Create'),
),
body: Column(
children: <Widget>[
Flexible(
child: PageView.builder(
controller: controller,
physics: NeverScrollableScrollPhysics(),
itemCount: _addPages.length,
itemBuilder: (context, index) {
return _addPages[index % _addPages.length];
},
),
),
],
),
);
}
}
It should be noted that you can only navigate in the PageView using 'Forward' and 'Back' buttons. And this is exactly where my problem lies. How can I set different conditions for forward and backward in the page children? I should be able to access the controller of the parent widget here somehow. Is that possible?
For example, how can I access the controller from this child widget?
class AddInformation extends StatefulWidget {
@override
_AddInformationState createState() => _AddInformationState();
}
class _AddInformationState extends State<AddInformation> {
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
alignment: Alignment.center,
height: 600,
child: Text('Some content here'),),
ButtonBar(
children: [
TextButton(
style: ButtonStyle(
),
onPressed: () {
if(/*<different conditions here, for example if form is valid*/ true)
{
} else{
//Scaffold.of(context).showSnackBar(snackBar);
}
},
child: Text(
'PREV',
style: TextStyle(
height: 16 / 14,
letterSpacing: 1.25,
fontSize: 14,
fontWeight: FontWeight.w500),
),
),
ElevatedButton(
style: ButtonStyle(
),
onPressed: () {
},
child: Text(
'NEXT',
style: TextStyle(
height: 16 / 14,
letterSpacing: 1.25,
fontSize: 14,
fontWeight: FontWeight.w500),
),
),
],
)
],
);
}
}
Thank you in advance!
Upvotes: 1
Views: 1854
Reputation: 266
For anyone who needs it, I found a different way by passing the whole controller.
With the ChangeNotifierProvider you can also access and edit the same data in every Page in PageView
class MyPageView extends StatefulWidget {
@override
_MyPageViewState createState() => _MyPageViewState();
}
class _MyPageViewState extends State<MyPageView> {
final controller = PageController(initialPage: 0);
Widget buildPageView() {
return Scaffold(
appBar: AppBar(
title: Text('AppBar'),
),
body: PageView(
controller: controller,
children: <Widget>[
Page1(pageController: controller,),
Page2(pageController: controller,),
],
),
);
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (context) => MyModel(),
child: buildPageView(),
);
}
}
In each page where you need the data add a Consumer:
class Page1 extends StatelessWidget {
final PageController pageController;
const Preview({
@required this.pageController,
Key key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Consumer<CreateAnnouncementModel>(
builder: (context, edit, child) {
return ListView(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Consumer<CreateAnnouncementModel>(
builder: (context, edit, child) {
return Text('My Data in Datamodel: ${edit.title}'),
ButtonBar(
children: [
TextButton(
style: ButtonStyle(),
onPressed: () {
pageController.previousPage(duration: Duration(milliseconds: 300), curve: Curves.ease);
},
child: Text(
'PREV',
),
),
ElevatedButton(
style: ButtonStyle(),
onPressed: () {
pageController.nextPage(duration: Duration(milliseconds: 300), curve: Curves.ease);
},
child: Text(
'NEXT',
),
),
],
)
],
);
}
);
}
}
And here the model:
class MyModel extends ChangeNotifier{
String title = '';
void updateTitle(String newTitle) {
title = newTitle;
notifyListeners();
}
}
Upvotes: 1
Reputation: 3298
You can control navigation through functions passed to your pages widgets as constructor parameters. Your code will be like this:
class AddNews extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AddNewsState();
}
class _AddNewsState extends State<AddNews> {
final controller = PageController(initialPage: 0);
List<Widget> _addPages;
@override
void initState() {
_addPages = [
SelectType(onTapNext: _onTapNext),
AddInformation(onTapPrevious: _onTapPrevious),
];
super.initState();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// Your build code...
}
void _onTapNext() => _animateToPage(1);
void _onTapPrevious() => _animateToPage(0);
void _animateToPage(int page) {
controller.animateToPage(
page,
duration: Duration(milliseconds: 500),
curve: Curves.linear,
);
}
}
class AddInformation extends StatefulWidget {
final Function onTapPrevious;
AddInformation({@required this.onTapPrevious});
@override
_AddInformationState createState() => _AddInformationState();
}
class _AddInformationState extends State<AddInformation> {
@override
Widget build(BuildContext context) {
return ... // Your widgets hierarchy here
TextButton(
style: ButtonStyle(),
onPressed: () {
if(someCondition) {
widget.onTapPrevious();
} else{
//Scaffold.of(context).showSnackBar(snackBar);
}
},
...
}
}
SelectType widget would be with onTapNext
parameter function, for example.
(!) Also pay attention to AddNews widget, it is statefull. It would be better to use statefull widget here because it stores PageController. Better to handle lifecycle of any controllers inside state.
Upvotes: 0