Une ventilation détaillée de la navigation dans Flutter

image



Flutter gagne en popularité parmi les développeurs. La plupart des approches de création d'applications ont déjà été établies et sont utilisées quotidiennement dans le développement d'applications de commerce électronique. Le thème de la navigation est ramené à l'arrière-plan ou troisième plan. Quelle API de navigation le Framework fournit-il? Quelles approches ont été développées? Comment ces approches sont-elles utilisées et à quoi servent-elles?



introduction



Commençons par qu'est-ce que la navigation? La navigation est une méthode qui vous permet de naviguer entre l'interface utilisateur avec des paramètres spécifiés.

Par exemple, dans le monde IOS, il organise la navigation UIViewController et dans Android - le composant Navigation. Que propose Flutter?





Les écrans de Flutter sont appelés route. Pour se déplacer entre les itinéraires, il existe une classe Navigator qui dispose d'une API complète pour implémenter divers types de navigation.





Commençons simplement. La navigation vers un nouvel écran (route) est appelée par la méthode push (), qui prend un argument - c'est Route.



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


Examinons de plus près l'appel de la méthode 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.



image



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


, :



  • BuildContext GlobalKey
  • route View




Flutter , .



:



Flutter Dev Podcast

Flutter




All Articles