问题描述
我有一个使用Flutter-Redux的应用程序.在某些用户操作上,将分派各种还原操作,并且这些操作在中间件中异步运行.我现在的问题是:万一出问题了,我该如何从中间件中显示SnackBar.我不能使用回调,因为不能保证派发动作的Widget仍然可用.错误将是:
I have an application that uses Flutter-Redux.On certain user actions various redux-actions are dispatched and those run asynchronously in a middleware.My question now is: how can I show a SnackBar from the middleware in case something goes wrong.I cannot use callbacks because it is not guaranteed that the Widget that dispatched the actions is still available. The error would be:
示例:
- 用户单击保存".
- 保存到数据库"操作是异步发送的.
- 节省剩余时间"操作是异步发送的.
- 对话框关闭.
现在...例如其余调用返回错误,由于很长一段时间"以来该对话框已关闭,其上下文无效,并且我无法显示SnackBar.
Now... When e.g. the rest call returns with an error, the dialog is closed since a "long" time, its context is invalid and I have no possibility to show a SnackBar.
进一步:
SnackBar必须始终绑定到脚手架.因此,我已经尝试制作一个空的root-Scaffold,并通过GlobalKey引用它.这就带来了一个问题,当另一个窗口小部件位于根窗口小部件上方且用户无法读取时,SnackBar被隐藏.
A SnackBar must always be bound to a Scaffold. So I already tried to make an empty root-Scaffold and reference it by a GlobalKey. This brings the problem that the SnackBar is hidden when another widget lies over the root widget and cannot be read by the user.
任何建议如何解决此问题?
Any suggestions how to solve this issue?
最诚挚的问候,弗洛里安
Best regards,Florian
推荐答案
redux在一次性错误"方面有点笨拙.一般来说,有两种处理方法:
redux is a bit clunky when it comes to "one-time errors". In general there are 2 ways to handle it:
- 您可以将错误保存在存储中,并在存储中存在错误时显示错误覆盖.从商店中删除错误以关闭覆盖.
- 将错误显示视为一次性"副作用(就像播放声音一样).我认为这是更好的解决方案,尤其是如果您要使用小吃店.
我不确定中间件的外观如何,但是在网络请求失败之后,您会将错误对象推送到rxdart Subject
或StreamController
中.现在,您有一个Stream
错误.
I'm not sure how exactly your middleware looks like, but after the network request failed, you would push the error object into a rxdart Subject
or StreamController
. Now you have a Stream
of errors.
作为StoreProvider
的直接子代,创建自己的包含错误流的InheritedWidget
,命名为SyncErrorProvider
:
As a direct child of your StoreProvider
, create your own InheritedWidget
that holds the stream of errors, named 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
.现在,您可以使用didChangeDependencies
中的SyncErrorProvider.of(context).errors
从任何路径访问错误流的简单方法.
The inherited widget should wrap your MaterialApp
. Now you have a simple way to access the stream of errors from any route, using SyncErrorProvider.of(context).errors
from didChangeDependencies
.
在小吃店中显示错误有点困难,因为小吃店的位置取决于页面布局(FAB,底部导航...),有时出现的小吃店会移动其他UI元素.
Displaying the error in a snackbar is a bit of a challenge, because the position of a snackbar depends on the page layout (FAB, bottom navigation...), and sometimes an appearing snackbar moves other UI elements.
处理快餐栏创建的最佳方法实际上取决于您的应用程序.我也不确定这些错误会多久发生一次,所以也许不要花太多时间在上面.
The best way to handle the snackbar creation really depends on your app. I'm also not sure how often these errors would occur, so maybe don't spend too much time on it.
两种方法各有利弊:
在每个具有脚手架的屏幕中,侦听错误流并在本地脚手架中显示小吃店.确保在取消放置小部件时退订.
In every screen that has a scaffold, listen to the stream of errors and display snackbars in the local scaffold. Make sure to unsubscribe when the widgets are disposed.
此方法的优势在于,小吃栏是页面UI的一部分,并将移动支架的其他元素.
Advantage of this approach is that the snackbars are a part of the page UI and will move other elements of the scaffold.
缺点是,如果存在对话框或没有支架的屏幕,则该错误将不可见.
Disadvantage is that if there are dialogs or screens without a scaffold, the error will not be visible.
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和底部条重叠.
Have a global scaffold for error snackbars
This scaffold would only be used for snackbars, nothing else. Advantage is that the errors are always guaranteed to be visible, disadvantage that they will overlap FABs and bottom bars.
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,
);
},
);
}
}
这篇关于当没有上下文可用时,如何从异步执行中显示SnackBar?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!