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?
Navegador
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.
Navegar para e retornar de uma nova rota
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 State
Navigator, MaterialApp/CupertinoApp/WidgetsApp. State API .
Stack. "call flow" NavigatorState.
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.
router.routeTo(RouteBundle(route: '/my_page', containerType: ContainerType.window));
, :
- BuildContext GlobalKey
- route View
Flutter , .