Ответ 1
Изменение: После нескольких месяцев с этим решением, я заметил, что есть несколько проблем с ним:
- Не работает аппаратная кнопка возврата Android
- Приложение сбрасывается при переключении режима "осмотра".
- Переходы невозможны
- Нет гарантии, что запрещенный маршрут не отображается
Поэтому я больше не рекомендую использовать этот подход!
Для обычной навигации по инициативе пользователя вам вообще не нужен шаблон BLoC. Просто используйте Navigator
.
Логин - это особый случай. Следуя шаблону BLoC, имеет смысл предоставить поток isAuthenticated
:
abstract class MyBloc {
Stream<bool> get isAuthenticated;
}
Вероятно, в вашем приложении будет 2 разных именованных дерева маршрутов: одно для зарегистрированных пользователей и одно для анонимных пользователей:
final Map<String, WidgetBuilder> anonymousRoutes = {
'/': (context) => new LoginScreen(), // default for anon
'/register': (context) => new RegisterScreen(),
};
final Map<String, WidgetBuilder> authenticatedRoutes = {
'/': (context) => new HomeScreen(), // default for logged in
'/savings': (context) => new SavingsScreen(),
// ...
};
Обычно Navigator
и его именованные маршруты тесно связаны с MaterialApp
, но вы также можете определить свой собственный, который будет перестроен при обновлении потока isAuthenticated
:
class MyApp extends StatelessWidget {
const MyApp({Key key, this.bloc}) : super(key: key);
final MyBloc bloc;
@override
Widget build(BuildContext context) {
return MaterialApp(
builder: (BuildContext context, Widget child) {
return StreamBuilder<bool>(
stream: bloc.isAuthenticated,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (!snapshot.hasData) {
return Text('loading...');
}
bool isAuthenticated = snapshot.data;
return _buildNavigator(isAuthenticated);
},
);
},
);
}
}
Navigator _buildNavigator(bool isAuthenticated) {
// different route tree and different default route depending on auth state
final routes = isAuthenticated ? authenticatedRoutes : anonymousRoutes;
return Navigator(
key: new ValueKey(isAuthenticated),
onGenerateRoute: (RouteSettings settings) {
final name = settings.name;
return new MaterialPageRoute(
builder: routes[name],
settings: settings,
);
},
onUnknownRoute: (RouteSettings settings) {
throw Exception('unknown route');
},
);
}
К сожалению, сейчас (2018-07-14) в коде Flutter есть 2 конфликтующих утверждения, которые необходимо удалить, чтобы приведенный выше код работал (вы можете просто отредактировать его с помощью своей IDE):
Строки 93 и 96 в packages\flutter\lib\src\widgets\app.dart
//assert(navigatorObservers != null),
//assert(onGenerateRoute != null || navigatorObservers == const <NavigatorObserver>[]),