我目前有一个有趣的问题,与基于用户操作显示快餐栏有关。
上面听起来很琐碎,但请详细说明:
我有2个屏幕:
应用程序使用块模式(带有stream / rxdart)。
这就是我想要的:
我尝试了几种实现方法:
添加一个新流(employeeAdded),然后在将雇员添加到雇员流中时,还向已添加的雇员中推送一个布尔值。
在员工列表中,添加新的流构建器,并在构建器逻辑中添加小吃栏。
这会带来各种各样的问题,例如试图在页面(重新)构建之前显示小吃栏,等等。
问题是双重的:在这种情况下,什么是好的UX实践,什么是解决该问题的好方法?
(将根据要求发布代码)
感谢您的帮助!
答案 0 :(得分:1)
@joey我找到了一个解决方案,但是我不确定这是最好的。我现在将我的应用程序更改为集团模式,所以我发现了与您相同的问题,所以我做到了:
在我的bloc文件上,仅将一个控制器用于scaffoldState:
ScaffoldState scaffold;
StreamController<ScaffoldState> _scaffoldController = StreamController<ScaffoldState>();
StreamSink<ScaffoldState> get setScaffold => _scaffoldController.sink;
然后在bloc文件的构造函数y上监听该流:
LoginBloc(){
_scaffoldController.stream.listen((sc){ scaffold = sc;});
}
在小部件上,只要想使用它,我都会沉入scaffoldState:
onPressed:() {
bloc.setScaffold.add(Scaffold.of(context));
}
然后,我可以像往常一样从集团展示小吃店:
scaffold.showSnackBar(SnackBar(content: Text(hi world'),),);
答案 1 :(得分:1)
有准备好的方格来显示小吃店。
基本上已经为此目的创建了BlockListener
。
请查看https://felangel.github.io/bloc/#/recipesfluttershowsnackbar
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
final dataBloc = BlocProvider.of<DataBloc>(context);
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: BlocListener(
bloc: dataBloc,
listener: (BuildContext context, DataState state) {
if (state is Success) {
Scaffold.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.green,
content: Text('Success'),
),
);
}
},
child: YourChild()
),
);
}
}
答案 2 :(得分:0)
尽管这不是我最喜欢的答案,但我也不确定是否有更好的选择。这是我为将来的观察者做的临时修复:
创建新的流(在我的示例中为employeeAdded),添加员工后,还会在流中创建一个条目:
final _employees = BehaviorSubject<List<Employee>>(seedValue: List<Employee>());
final _employeeAdded = BehaviorSubject();
// streams (out)
Observable<List<Employee>> get employees => _employees.stream;
Observable<dynamic> get employeesAdded => _employeeAdded.stream;
addEmployee(Employee employee) async {
final newList = <Employee>[]..addAll(_employees.value)..add(employee);
await _employeeRepo.upsertEmployee(employee);
_employees.add(newList);
_employeeAdded.add(true);
//FIXME: Save to db
}
请注意,employeeAdded中没有seedValue,这是为了防止小吃栏在初始加载时显示。
在我的屏幕/页面上有一个脚手架;它的主体调用另一个方法,该方法应解释其余代码:
Widget _buildBody(EmployeeBloc bloc) {
return StreamBuilder(
stream: bloc.employees,
builder: (context, snapshot) {
if (!snapshot.hasData) {
bloc.employeesAdded.listen(
(_) => Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Employee added'),
),
),
);
bloc.seedEmployees();
return Center(
child: Text("No employees"),
);
}
return _buildList(bloc, snapshot.data);
},
);
}
使用hasData if记录对集团的监听。
目前可以使用,但想知道是否还有更简洁的示例。
答案 3 :(得分:0)
用select distinct x
from (select 'a' as x union all
select 'a '
) y;
将body
(Scaffold
的孩子,在您的情况下,_buildBody
返回)包装成Builder
,这样您就可以使用context
。
现在,您可以使用提供的处理程序来监听流(就像您已经做过的那样),但是这次您每次Scaffold
构建都只注册一次监听器(并且不会在{中发生的每个事件中注册监听器) {1}}流)。
employeesAdded
答案 4 :(得分:0)
我已经为此苦苦挣扎了两个星期,我怀疑对实际问题有误解。
有多个路线(页面)正在收听同一广播事件,并且当用户以一种干净的方式向前和向后导航时,我们无法暂停和继续这些事件。在这种情况下,多条路线是由用户在导航到新屏幕时引起的,旧屏幕在后台仍然可用并接收事件,但是直到用户之前,其动画无法显示SnackBar
点击 后退按钮。
使用SnackBar
中的以下内容,仅在最高路由(这是用户正在查看的路由)上显示StatefulWidget
:
...
@override
void initState() {
_subscription = employeeStream.listen((event) {
if (ModalRoute.of(context).isCurrent) { // Check the top most route
_l.fine('Only showing message on top');
Scaffold.of(context).showSnackBar(...);
} else { // Ignore all other routes
_l.fine('Ignoring event');
}
});
super.initState();
}
...
答案 5 :(得分:-1)
您可以在构建器中尝试以下操作:
WidgetsBinding.instance.addPostFrameCallback((_) => showSnackBar(message));