Florian
Florian

Reputation: 266

Controll PageView with Buttons with various conditions

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

Answers (2)

Florian
Florian

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

Mol0ko
Mol0ko

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

Related Questions