Replace widgets like fragments in Flutter

I'm not sure if you can use the router to replace just the part of a view; but you could conditionally change which Widget you render in the build method, like this:

children: <Widget>[
    someCondition ? new FragmentA() : new FragmentChildA(),
    new FragmentB()
],

Then you just need to set someCondition by using setState in the stateful widget:

setState(() => someCondition = true);

If you want to do this from inside FragmentA you could allow it to have the function passed into its constructor:

new FragmentA(
  onPress: setState(() => someCondition = true)
)

However, it might be better to encapsulate all of this logic inside a single widget so this logic isn't all hanging around in the parent. You could make a single StatefulWidget for FragementA which keeps track of which stage you're on, and then in its build method renders the correct child widget, something like:

build() {
  switch(stage) {
    Stages.Stage1:
      return new Stage1(
        onNext: () => setState(() => stage = Stages.Stage2);
      );
    Stages.Stage2:
      return new Stage1(
        onPrevious: () => setState(() => stage = Stages.Stage1);
      );
  }
}

Well, I found out the way to handle this case for a few months, but I just forgot to answer this question.

The solution is wrapping your Widget with a new Navigator.

You can see the video example here

And the simple demo for it here

The downside of this solution is sometimes, the keyboard is not showing as my intention.


ok I'm going to be doing this the same way google does it with the bottom navigation bar, I don't see this as the most performant but it works

  class MainFabContainer extends StatefulWidget {

    MainFabContainer({
      Key key,
    }) : super(key: key);

    @override
    State<StatefulWidget> createState() {
      return MainFabContainerState();
    }

  }

  class MainFabContainerState extends State<MainFabContainer> {
    String title = "Title";
    int _currentIndex = 0;
    final List<int> _backstack = [0];

    @override
    Widget build(BuildContext context) {
      //each fragment is just a widget which we pass the navigate function
      List<Widget> _fragments =[Fragment1(navigate: navigateTo),Fragment2(navigate: navigateTo,),Fragment3(navigate: navigateTo,)];
      //will pop scope catches the back button presses
      return WillPopScope(
        onWillPop: () {
          customPop(context);
        },
        child: Scaffold(
          drawer: drawer(),
          appBar: AppBar(
            title: Text(title),
          ),
          body: Column(
            children: <Widget>[
              Expanded(
                child: _fragments[_currentIndex],
              ),
            ],
          ),
        ),
      );
    }


    void navigateTo(int index) {
      _backstack.add(index);
      setState(() {
        _currentIndex = index;
      });
    }

    void navigateBack(int index) {
      setState(() {
        _currentIndex = index;
      });
    }

    customPop(BuildContext context) {
      if (_backstack.length - 1  > 0) {
        navigateBack(_backstack[_backstack.length - 1]);
      } else {
        _backstack.removeAt(_backstack.length - 1);
        Navigator.pop(context);
      }
    }
    //this method could be called by the navigate and navigate back methods
    _setTitle(String appBarTitle) {
      setState(() {
        title = appBarTitle;
      });
    }
  }

You could simply use a MaterialApp widget with the CupertinoPageTransitionsBuilder as pageTransitionTheme like

MaterialApp(
   theme: ThemeData(
   pageTransitionsTheme: PageTransitionsTheme(builders: {
   TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
   TargetPlatform.android: SlideRightPageTransitionsBuilder(),
   }),
   initialRoute: "fragment1",
   routes: <String, WidgetBuilder>{
      "fragment1": (BuildContext context) => Fragment1(),
      "fragment2": (BuildContext context) => Fragment2(),
   }
...
),

Then in fragment 1 you simply use the following to navigate to the other fragment with a slide animation

Navigator.of(context).pushReplacementNamed("fragment2");