Uma anĆ”lise detalhada da navegaĆ§Ć£o no Flutter

imagem



Flutter estĆ” ganhando popularidade entre os desenvolvedores. A maioria das abordagens para a criaĆ§Ć£o de aplicativos jĆ” foi estabelecida e Ć© usada diariamente no desenvolvimento de aplicativos de comĆ©rcio eletrĆ“nico. O tĆ³pico de navegaĆ§Ć£o Ć© rebaixado para o plano de fundo ou terceiro plano. Qual API de navegaĆ§Ć£o o Framework fornece? Que abordagens foram desenvolvidas? Como essas abordagens sĆ£o usadas e para que servem?



IntroduĆ§Ć£o



Vamos comeƧar com o que Ć© navegaĆ§Ć£o? A navegaĆ§Ć£o Ć© um mĆ©todo que permite navegar entre a interface do usuĆ”rio com parĆ¢metros especificados.

Por exemplo, no mundo IOS, o UIViewController organiza a navegaĆ§Ć£o, e no Android - o componente NavegaĆ§Ć£o. O que o Flutter oferece?





As telas no Flutter sĆ£o chamadas de rota. Para navegar entre as rotas, existe uma classe Navigator que possui uma extensa API para a implementaĆ§Ć£o de vĆ”rios tipos de navegaĆ§Ć£o.





Vamos comeƧar de forma simples. A navegaĆ§Ć£o para uma nova tela (rota) Ć© chamada pelo mĆ©todo push (), que tem um argumento - este Ć© Route.



Navigator.push(MaterialPageRoute(builder: (BuildContext context) => MyPage()));


Vamos dar uma olhada mais de perto na chamada do mƩtodo push:



Navigator ā€” , .

Navigator.push() ā€” route   .

MaterialPageRoute() ā€” route,   .

builder ā€” MaterialPageRoute, .

MyPage ā€” Stateful/Stateless Widget



route



pop().



Navigator.pop();




builder . Navigator, build.



Navigator.push(context, MaterialPageRoute(builder: (context) => MyPage(someData: data)));


MyPage ( ).



pop() .



Navigator.pop(data);




Navigator, MaterialApp/CupertinoApp/WidgetsApp. State API .

Stack. "call flow" NavigatorState.



https://habrastorage.org/webt/5w/dg/nb/5wdgnb-tjlngub4c8y4rlpqkeqi.png



vs



. route, .



:



, ā€” ?

: , . .



, ā€” ?

: , .



Imperative vs Declarative Programming





. . Route. Flutter route, MaterialPageRoute CupertinoPageRoute. CupertinoPageRoute title, settings.



:



Navigator.push(
    context,
    CupertinoPageRoute<T>(
        title: "Setting",
        builder: (BuildContext context) => MyPage(),
        settings: RouteSettings(name:"my_page"),
    ),
);


route   ViewModel/Controller/BLoC/ā€¦ .



MyPage CupertinoPageRoute. push .



:



, route .




. .



:



Navigator.pushNamed(context, '/my_page');


. " " .

MaterialApp/CupertinoApp/WidgetsApp. 2 onGenerateRoute onUnknownRoute .



:



MaterialApp(
    onUnknownRoute: (settings) => CupertinoPageRoute(
      builder: (context) {
                return UndefinedView(name: settings.name);
            }
    ),
  onGenerateRoute: (settings) {
    if (settings.name == '\my_page') {
      return CupertinoPageRoute(
                title: "MyPage",
                settings: settings,
        builder: (context) => MyPage(),
      );
    }
        //     
  },
);


:

onGenerateRoute ā€” Navigator.pushNamed(). route.

onUnknownRoute ā€” onGenerateRoute null. route, web ā€” 404 page.



. , .




. .



:



  • showAboutDialog
  • showBottomSheet
  • showDatePicker
  • showGeneralDialog
  • showMenu
  • showModalBottomSheet
  • showSearch
  • showTimePicker
  • showCupertinoDialog
  • showDialog
  • showLicensePage
  • showCupertinoModalPopup


. API, .



?



, showGeneralDialog.



:



Future<T> showGeneralDialog<T>({
  @required BuildContext context,
  @required RoutePageBuilder pageBuilder,
  bool barrierDismissible,
  String barrierLabel,
  Color barrierColor,
  Duration transitionDuration,
  RouteTransitionsBuilder transitionBuilder,
  bool useRootNavigator = true,
  RouteSettings routeSettings,
}) {
  assert(pageBuilder != null);
  assert(useRootNavigator != null);
  assert(!barrierDismissible || barrierLabel != null);
  return Navigator.of(context, rootNavigator: useRootNavigator).push<T>(_DialogRoute<T>(
    pageBuilder: pageBuilder,
    barrierDismissible: barrierDismissible,
    barrierLabel: barrierLabel,
    barrierColor: barrierColor,
    transitionDuration: transitionDuration,
    transitionBuilder: transitionBuilder,
    settings: routeSettings,
  ));
}


. showGeneralDialog push NavigatorState _DialogRoute(). , .



ā€” route .


route



 "every thins is a route", . , route .



route Flutter ā€” PageRoute PopupRoute.



PageRoute ā€” route, .

PopupRoute ā€” route, route.



PageRoute:



  • MaterialPageRoute
  • CupertinoPageRoute
  • _SearchPageRoute
  • PageRouteBuilder


PopupRoute:



  • _ContexMenuRoute
  • _DialogRoute
  • _ModalBottomSheetRoute
  • _CupertinoModalPopupRoute
  • _PopupMenuRoute


PopupRoute , .



:



, .


Best practices



, " ?". , API , . .



:



  • .
  • BuildContext ( , BuildContext).
  • . route, CupertinoPageRoute, BottomSheetRoute, DialogRoute ..


:



abstract class IRouter {
  Future<T> routeTo<T extends Object>(RouteBundle bundle);
  Future<bool> back<T extends Object>({T data, bool rootNavigator});
  GlobalKey<NavigatorState> rootNavigatorKey;
}


:

routeTo - .

back ā€” .

rootNavigatorKey ā€” GlobalKey NavigatorState.

, .



class Router implements IRouter {
    @override
  GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>();

    @override
  Future<T> routeTo<T>(RouteBundle bundle) async {
   // Push logic here
  }

    @override
  Future<bool> back<T>({T data, bool rootNavigator = false}) async {
        // Back logic here
    }
}


 , routeTo().



@override
  Future<T> routeTo<T>(RouteBundle bundle) async {
        assert(bundle != null, "The bundle [RouteBundle.bundle] is null");
    NavigatorState rootState = rootNavigatorKey.currentState;
    assert(rootState != null, 'rootState [NavigatorState] is null');

    switch (bundle.route) {
      case "/routeExample":
        return await rootState.push(
          _buildRoute<T>(
            bundle: bundle,
            child: RouteExample(),
          ),
        );

      case "/my_page":
        return await rootState.push(
          _buildRoute<T>(
            bundle: bundle,
            child: MyPage(),
          ),
        );
      default:
        throw Exception('Route is not found');
    }
  }


root NavigatorState ( WidgetsApp) push RouteBundle .



RouteBundle. , .



enum ContainerType {
  /// The parent type is [Scaffold].
  ///
  /// In IOS route with an iOS transition [CupertinoPageRoute].
  /// In Android route with an Android transition [MaterialPageRoute].
  ///
  scaffold,

  /// Used for show child in dialog.
  ///
  /// Route with [DialogRoute].
  dialog,

  /// Used for show child in [BottomSheet].
  ///
  /// Route with [ModalBottomSheetRoute].
  bottomSheet,

  /// Used for show child only.
  /// [AppBar] and other features is not implemented.
  window,
}
class RouteBundle {
  /// Creates a bundle that can be used for [Router].
  RouteBundle({
    this.route,
    this.containerType,
  });

  /// The route for current navigation.
  ///
  /// See [Routes] for details.
  final String route;

  /// The current status of this animation.
  final ContainerType containerType;
}


enum ContainerType ā€” , .

RouteBundle ā€” - route.



_buildRoute. , route .



Route<T> _buildRoute<T>({@required RouteBundle bundle, @required Widget child}) {
    assert(bundle.containerType != null, "The bundle.containerType [RouteBundle.containerType] is null");

    switch (bundle.containerType) {
      case ContainerType.scaffold:
        return CupertinoPageRoute<T>(
          title: bundle.title,
          builder: (BuildContext context) => child,
          settings: RouteSettings(name: bundle.route),
        );
      case ContainerType.dialog:
        return DialogRoute<T>(
          title: '123',
          settings: RouteSettings(name: bundle.route),
          pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
                        return child;
                    },
        );
      case ContainerType.bottomSheet:
        return ModalBottomSheetRoute<T>(
          settings: RouteSettings(name: bundle.route),
          isScrollControlled: true,
          builder: (BuildContext context) => child,
        );
      case ContainerType.window:
        return CupertinoPageRoute<T>(
          settings: RouteSettings(name: bundle.route),
          builder: (BuildContext context) => child,
        );
      default:
        throw Exception('ContainerType is not found');
    }
  }


ModalBottomSheetRoute DialogRoute, . route Material Flutter.



back.



@override
Future<bool> back<T>({T data, bool rootNavigator = false}) async {
    NavigatorState rootState = rootNavigatorKey.currentState;
  return await (rootState).maybePop<T>(data);
}


rootNavigatorKey App :



MaterialApp(
    navigatorKey: widget.router.rootNavigatorKey,
    home: Home()
);


, route. - "" , , Dependency Injection.



imagem



router.routeTo(RouteBundle(route: '/my_page', containerType: ContainerType.window));


, :



  • BuildContext GlobalKey
  • route View




Flutter , .



:



Flutter Dev Podcast

Flutter




All Articles