31

Hello I tried to jump to my second page tab if I press on a button on my first Page tab. Currently I know only call route of my seconde page widget but bottomnavbar isn't present... I don't know how to call my parent widget from my first page tab to jump to the seconde page tab.

  class Parent  {

  int bottomSelectedIndex = 0;

  List<BottomNavigationBarItem> buildBottomNavBarItems() {
    return [
      BottomNavigationBarItem(
          icon: new Icon(Icons.home),
          title: new Text('First')
      ),
      BottomNavigationBarItem(
        icon: new Icon(Icons.search),
        title: new Text('Second'),
      ),

    ];
  }

  PageController pageController = PageController(
    initialPage: 0,
    keepPage: true,
  );

  Widget buildPageView() {
    return PageView(
      controller: pageController,
      onPageChanged: (index) {
        pageChanged(index);
      },
      children: <Widget>[
        First(),
        Second(),

      ],
    );
  }

  @override
  void initState() {
    super.initState();
  }

  void pageChanged(int index) {
    setState(() {
      bottomSelectedIndex = index;
    });
  }

  void bottomTapped(int index) {
    setState(() {
      bottomSelectedIndex = index;
      pageController.animateToPage(index, duration: Duration(milliseconds: 500), curve: Curves.ease);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: buildPageView(),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: bottomSelectedIndex,
        onTap: (index) {
          bottomTapped(index);
        },
        items: buildBottomNavBarItems(),
      ),
    );
  }
  }

  class first {
        return Container(
    // here a pressbutton for jump to the second widget
        );

    }

----------------------------------------------------------


    class second
        return Container(

        );
    }
LorenzOliveto
  • 7,796
  • 1
  • 20
  • 47
Nitneuq
  • 3,866
  • 13
  • 41
  • 64
  • Hi can you be more specific what are you looking for? and what is the issue your facing. If there is any error log then do post that too. – Vicky Salunkhe Jun 11 '19 at 09:05

2 Answers2

56

You can use this:

void onAddButtonTapped(int index) {

  // use this to animate to the page
  pageController.animateToPage(index);

  // or this to jump to it without animating
  pageController.jumpToPage(index);
}

Pass the function as params:

class first {
  final void Function(int) onAddButtonTapped;

        return Container(
// call it here onAddButtonTapped(2);
        );

    }

class Second {
  final void Function(int) onAddButtonTapped;

        return Container(
        );

    }
children: <Widget>[
        First(onAddButtonTapped),
        Second(onAddButtonTapped),
      ],

Mahesh Jamdade
  • 17,235
  • 8
  • 110
  • 131
Taym95
  • 2,331
  • 13
  • 14
  • Thank you, but if I understand I can call onAddButtonTapped() only if I am in my parent widget with Pagecontroller declared. My problem is that I don't know how to call this new page from a child page. – Nitneuq Jun 11 '19 at 09:12
  • Thank you it works :) but have you an idea if onButtonPressed comes from a widget who isn't in PageView() [ ] I have an other stfl widget I open with routes, and onPressed I want close this widget and jump to the second page :) I understand the pass of the callback but I don't understand how I can call this callback in this case – Nitneuq Jun 11 '19 at 11:42
  • 1
    You can upvote the answer since it works then you can ask about another question! – Taym95 Jun 11 '19 at 12:02
  • it's an old one but saved my day! thanks :) – Val May 07 '21 at 14:27
20

You can pass a callback to your first widget and call that when the button is pressed, so you can change the page in the parent widget. Something like this:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Test',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Parent(),
    );
  }
}

class Parent extends StatefulWidget {
  @override
  _ParentState createState() => _ParentState();
}

class _ParentState extends State<Parent> {
  int bottomSelectedIndex = 0;

  List<BottomNavigationBarItem> buildBottomNavBarItems() {
    return [
      BottomNavigationBarItem(
          icon: new Icon(Icons.home), title: new Text('First')),
      BottomNavigationBarItem(
        icon: new Icon(Icons.search),
        title: new Text('Second'),
      ),
    ];
  }

  PageController pageController = PageController(
    initialPage: 0,
    keepPage: true,
  );

  Widget buildPageView() {
    return PageView(
      controller: pageController,
      onPageChanged: (index) {
        pageChanged(index);
      },
      children: <Widget>[
        FirstWidget(
          onButtonPressed: () => pageController.animateToPage(
                1,
                duration: Duration(milliseconds: 300),
                curve: Curves.linear,
              ),
        ),
        SecondWidget(),
      ],
    );
  }

  @override
  void initState() {
    super.initState();
  }

  void pageChanged(int index) {
    setState(() {
      bottomSelectedIndex = index;
    });
  }

  void bottomTapped(int index) {
    setState(() {
      bottomSelectedIndex = index;
      pageController.animateToPage(index,
          duration: Duration(milliseconds: 500), curve: Curves.ease);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Test'),
      ),
      body: buildPageView(),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: bottomSelectedIndex,
        onTap: (index) {
          bottomTapped(index);
        },
        items: buildBottomNavBarItems(),
      ),
    );
  }
}

class FirstWidget extends StatefulWidget {
  final VoidCallback onButtonPressed;

  FirstWidget({@required this.onButtonPressed});

  @override
  _FirstWidgetState createState() => _FirstWidgetState();
}

class _FirstWidgetState extends State<FirstWidget> {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.red,
      child: Center(
        child: FlatButton(
          onPressed: widget.onButtonPressed,
          child: Text('Go to second page'),
        ),
      ),
    );
  }
}


class SecondWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(color: Colors.green);
  }
}
LorenzOliveto
  • 7,796
  • 1
  • 20
  • 47
  • I tried but onButtonPressed isn't recognize... I'm very noob ^^ I think I don't understand how to use callback – Nitneuq Jun 11 '19 at 09:32
  • What error is giving you? Have you declared the property in your First class? Have you added a constructor? First is a Stateless or Stateful Widget? 'VoidCallback' is just an alias for 'void Function()', you pass a function that can be called whenever you want. – LorenzOliveto Jun 11 '19 at 09:34
  • undefined name 'onButtonPressed' with the line onPressed: onButtonPressed, I follow the exemple with call back in the constructor, first is a stfl widget. – Nitneuq Jun 11 '19 at 09:45
  • 2
    I updated the answer converting First to a stateful widget, the callback needs to be on the widget and not in the state. – LorenzOliveto Jun 11 '19 at 09:51
  • What if I need to add another button too. Like in my case 'Next' and 'Previous' buttons.. ? @LorenzOliveto – Joe May 18 '20 at 10:27
  • @Joe you can still use pageController.animateToPage(...) giving the page you want to scroll to as a parameter – LorenzOliveto May 18 '20 at 12:12
  • Yes your code is working and it guided me to find solution which I'm looking for days to add two buttons in the child. – Joe May 18 '20 at 12:39