Как показать SnackBar из асинхронных исполнений, когда контекст недоступен? - PullRequest
0 голосов
/ 07 сентября 2018

У меня есть приложение, которое использует Flutter-Redux. При определенных действиях пользователя отправляются различные избыточные действия, которые выполняются асинхронно в промежуточном программном обеспечении. Теперь у меня вопрос: как я могу показать SnackBar из промежуточного программного обеспечения, если что-то пойдет не так. Я не могу использовать обратные вызовы, потому что не гарантируется, что виджет, отправивший действия, по-прежнему доступен. Ошибка будет:

На данный момент состояние дерева элементов виджета больше не является стабильный. Чтобы безопасно ссылаться на предка виджета в его распоряжении () метод, сохранить ссылку на предка, вызвав inheritFromWidgetOfExactType () в didChangeDependencies виджета () способ.

Пример:

  1. Пользователь нажимает кнопку «Сохранить».
  2. Действие «Сохранить в БД» отправляется асинхронно.
  3. Действие «Сохранить поверх остальных» отправляется асинхронно.
  4. Диалог закрыт.

Сейчас ... Когда, например, вызов rest возвращается с ошибкой, диалоговое окно закрывается с «долгого времени», его контекст недействителен, и у меня нет возможности показать SnackBar.

Далее:

Закусочная всегда должна быть привязана к эшафоту. Поэтому я уже пытался создать пустой корневой скаффолд и ссылаться на него с помощью GlobalKey. Это приводит к проблеме, заключающейся в том, что SnackBar скрыт, когда другой виджет лежит над корневым виджетом и не может быть прочитан пользователем.

Есть предложения, как решить эту проблему?

С уважением, Florian

1 Ответ

0 голосов
/ 08 сентября 2018

redux немного неуклюже, когда дело доходит до «одноразовых ошибок». В общем, есть 2 способа справиться с этим:

  1. Вы можете сохранить ошибки в хранилище и отобразить наложение ошибок, пока в магазине существует ошибка. Удалите ошибку из магазина, чтобы закрыть оверлей.
  2. Рассматривать отображение ошибки как «одноразовый» побочный эффект (как при воспроизведении звука). Я думаю, что это лучшее решение, особенно если вы хотите использовать закусочные.

Я не уверен, как именно выглядит ваше промежуточное ПО, но после сбоя сетевого запроса вы поместите объект ошибки в rxdart Subject или StreamController. Теперь у вас Stream ошибок.

Как прямой потомок вашего StoreProvider, создайте свой собственный InheritedWidget, который содержит поток ошибок с именем SyncErrorProvider:

class SyncErrorProvider extends InheritedWidget {
  const SyncErrorProvider({Key key, this.errors, @required Widget child})
      : assert(child != null),
        super(key: key, child: child);

  final Stream<Object> errors;

  static SyncErrorProvider of(BuildContext context) {
    return context.inheritFromWidgetOfExactType(SyncErrorProvider) as SyncErrorProvider;
  }

  @override
  bool updateShouldNotify(SyncErrorProvider old) => errors != old.errors;
}

Унаследованный виджет должен обернуть ваш MaterialApp. Теперь у вас есть простой способ получить доступ к потоку ошибок с любого маршрута, используя SyncErrorProvider.of(context).errors из didUpdateDependencies.


Отображение ошибки в снэк-баре представляет собой сложную задачу, потому что положение снэк-бара зависит от макета страницы (FAB, нижняя навигация ...), и иногда появляющийся снэк-бар перемещает другие элементы пользовательского интерфейса.

Лучший способ создания закусочной зависит от вашего приложения. Я также не уверен, как часто эти ошибки будут появляться, поэтому, возможно, не тратьте на это слишком много времени.

Два разных подхода с преимуществами и недостатками:

Отображать ошибки в каркасах страниц

На каждом экране, на котором есть эшафот, слушайте поток ошибок и отображайте закусочные в локальном эшафоте. Обязательно отмените подписку при удалении виджетов.

Преимущество этого подхода заключается в том, что закусочные являются частью пользовательского интерфейса страницы и будут перемещать другие элементы каркаса.

Недостатком является то, что при наличии диалогов или экранов без каркаса ошибка не будет видна.

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  StreamSubscription _errorsSubscription;
  final _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    if(_errorsSubscription == null) {
      _errorsSubscription = SyncErrorProvider.of(context).errors.listen((error) {
        _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text(error.toString())));
      });
    }
  }

  @override
  void dispose() {
    _errorsSubscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      body: ...,
    );
  }
}

Иметь глобальные леса для ошибочных закусочных

Этот эшафот будет использоваться только для закусочных, ничего больше. Преимущество состоит в том, что ошибки всегда гарантированно видны, недостатком является то, что они будут перекрывать FAB и нижние столбцы.

class MyApp extends StatefulWidget {
  final Stream<Object> syncErrors; // coming from your store/middleware

  MyApp({Key key, this.syncErrors}) : super(key: key);
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  StreamSubscription _errorsSubscription;
  final _errorScaffoldKey = GlobalKey<ScaffoldState>();

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _errorsSubscription = widget.syncErrors.listen((error) {
      _errorScaffoldKey.currentState.showSnackBar(SnackBar(content: Text(error.toString())));
    });
  }

  @override
  void dispose() {
    _errorsSubscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      builder: (context, child) {
        Scaffold(
          key: _errorScaffoldKey,
          body: child,
        );
      },
    );
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...