У меня есть веб-приложение, написанное на Flutter, в котором маршруты не обновляются, как я ожидал.
Контекст
Приложение изначально было написано для мобильных устройств, но теперь я пытаюсь преобразовать его в веб-приложение. Базовая c архитектура приложения не изменилась, но поведение приложения на Chrome не такое, как на Android / iOS. Я объясню ниже.
Архитектура
Инициализация приложения происходит следующим образом:
- Функция верхнего уровня
runApp
возвращает класс «_ServiceLayer», который в основном просто создает набор служб, к которым можно получить доступ в приложении через интерфейс поставщика. В моем упрощенном примере я только что создал одну службу, которая увеличивает счетчик. - Следующий уровень - это виджет «_Dispatcher», который по сути определяет состояние приложения из одной из служб.
- В зависимости от состояния приложения я возвращаю MaterialApp с другим initialRoute. Если состояние приложения изменяется, _Dispatcher прослушивает и сгенерирует новое приложение MaterialApp с другим initialRoute.
Рабочий пример
// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
return runApp(_ServiceLayer());
}
class CounterService extends ChangeNotifier {
int counter = 0;
increment() {
counter += 1;
notifyListeners();
}
}
class _ServiceLayer extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => CounterService(), lazy: false),
],
child: _Dispatcher(),
);
}
}
class _Dispatcher extends StatelessWidget {
@override
Widget build(BuildContext context) {
_app(String initialRoute) {
return myMaterialApp(initialRoute);
}
int current_value = context.select((CounterService c) => c.counter);
if (current_value % 2 == 0) return _app('even');
if (current_value % 2 == 1) return _app('odd');
return _app('error'); // should never get here, so did not implement
}
}
class myMaterialApp extends StatelessWidget {
String initialRoute;
myMaterialApp(this.initialRoute);
@override
Widget build(BuildContext context) {
print("Generating $initialRoute route"); // <-- ALTERNATES EVEN & ODD
return MaterialApp(
title: 'Example',
initialRoute: initialRoute,
routes: {
'even': (BuildContext context) => EvenPage(),
'odd': (BuildContext context) => OddPage(),
},
);
}
}
class EvenPage extends Page {
String title = 'Even';
}
class OddPage extends Page {
String title = 'Odd';
}
abstract class Page extends StatelessWidget {
String title;
@override
Widget build(BuildContext context) {
var counterService = Provider.of<CounterService>(context, listen: false);
int ctr = counterService.counter;
return Column(
children: [
Text(title), // <-- ALWAYS SAYS EVEN
Text("Counter = ${ctr}"),
RaisedButton(
child: Text("Press me"),
onPressed: () {
counterService.increment();
}
),
],
);
}
}
Проблема
Проблема заключается в следующем: если вы несколько раз нажимаете кнопку, счетчик будет увеличиваться, как ожидалось, а операторы печати в консоли будут чередоваться между четным и нечетным:
Generating even route
Generating odd route
Generating even route
etc.
Это, похоже, означает, что создается приложение MaterialApp заново с каждой кнопкой pu sh, что является ожидаемым поведением. Однако , заголовок страницы всегда "Четный", как будто OddPage вообще никогда не строится!
Flutter Doctor
Результат работы Flutter Doctor:
[✓] Flutter (Channel beta, 1.20.0-7.4.pre, on Mac OS X 10.15.3 19D76, locale en-US)
• Flutter version 1.20.0-7.4.pre at ...
• Framework revision 916c3ac648 (3 days ago), 2020-08-01 09:01:12 -0700
• Engine revision d6ee1499c2
• Dart version 2.9.0 (build 2.9.0-21.10.beta)
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
• Android SDK at ...
• Platform android-29, build-tools 28.0.3
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 11.6)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 11.6, Build version 11E708
• CocoaPods version 1.8.4
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 4.0)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin version 47.1.2
• Dart plugin version 193.7361
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6222593)
[✓] Connected device (2 available)
• Web Server (web) • web-server • web-javascript • Flutter Tools
• Chrome (web) • chrome • web-javascript • Google Chrome 84.0.4147.105
• No issues found!
Вопросы
- Что происходит? Создается новый MaterialApp или нет?
- Предлагаете ли вы другой дизайн? Я подумал, нужно ли сгенерировать один MaterialApp с initialRoute, эффективно выполняющим работу _Dispatcher, но по причинам, которые я не помню, я не преследовал его. Может мне стоит пересмотреть этот подход?