基于流的拍打小吃条显示

时间:2019-01-03 13:25:54

标签: flutter rxdart

我目前有一个有趣的问题,与基于用户操作显示快餐栏有关。

上面听起来很琐碎,但请详细说明:

我有2个屏幕:

  1. 员工名单
  2. 添加员工

应用程序使用块模式(带有stream / rxdart)。

这就是我想要的:

  • 用户单击“雇员列表”屏幕中的“添加雇员FAB”按钮,然后导航到“添加雇员”屏幕(工作正常)
  • 用户填写员工详细信息,然后单击保存
  • 保存后,将雇员添加到流中,并更新了“雇员列表”屏幕(工作正常)
  • 将用户导航回雇员列表(工作正常)
  • 显示小吃店,表明已成功添加员工(这是问题)

我尝试了几种实现方法:

添加一个新流(employeeAdded),然后在将雇员添加到雇员流中时,还向已添加的雇员中推送一个布尔值。

在员工列表中,添加新的流构建器,并在构建器逻辑中添加小吃栏。

这会带来各种各样的问题,例如试图在页面(重新)构建之前显示小吃栏,等等。

问题是双重的:在这种情况下,什么是好的UX实践,什么是解决该问题的好方法?

(将根据要求发布代码)

感谢您的帮助!

6 个答案:

答案 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; bodyScaffold的孩子,在您的情况下,_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));