导航到新页面时防止颤动块状态更改

Posted

技术标签:

【中文标题】导航到新页面时防止颤动块状态更改【英文标题】:Prevent flutter bloc state changes when navigate to new page 【发布时间】:2020-11-01 02:30:42 【问题描述】:

我是使用 flutter_bloc 的新手,目前在我的项目中遇到问题。

所以,我有这样的 bloc 类:

class TestBloc extends Bloc<TestEvent, TestState> 
  @override
  TestState get initialState => InitialTestState();

  @override
  Stream<TestState> mapEventToState(
    TestEvent event,
  ) async* 
    if (event is StateOneEvent) 
      yield StateOne("one");
     else if (event is StateTwoEvent) 
      yield StateTwo("two");
    
  


/// === Event class
abstract class TestEvent extends Equatable 
  const TestEvent();


class StateOneEvent extends TestEvent 
  @override
  List<Object> get props => [];


class StateTwoEvent extends TestEvent 
  @override
  List<Object> get props => [];



// == State class
abstract class TestState extends Equatable 
  const TestState();


class InitialTestState extends TestState 
  @override
  List<Object> get props => [];


class StateOne extends TestState 
  final String value;

  StateOne(this.value);

  @override
  List<Object> get props => [value];


class StateTwo extends TestState 
  final String value;

  StateTwo(this.value);

  @override
  List<Object> get props => [value];

这是我的主要功能:

void main() 
  runApp(
    MultiBlocProvider(
      providers: [
        BlocProvider(create: (context) 
          return TestBloc();
        ),
      ],
      child: MyApp(),
    ),
  );


class MyApp extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      initialRoute: '/',
      routes: 
        '/': (context) => FirstPage(),
        '/second_page': (context) => SecondPage(),
      ,
    );
  

所以在这里我有两个页面,FirstPage 和 SecondPage 就像这里一样。每个此类都将显示该集团的当前状态。

class FirstPage extends StatefulWidget 
  @override
  _FirstPageState createState() => _FirstPageState();


class _FirstPageState extends State<FirstPage> 
  @override
  void initState() 
    super.initState();
    BlocProvider.of<TestBloc>(context).add(StateOneEvent());
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(body: BlocBuilder<TestBloc, TestState>(
      builder: (_, state) 
        String value = '-';
        if (state is StateOne) 
          value = state.value;
         else if (state is StateTwo) 
          value = state.value;
        
        return Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(value),
              RaisedButton(
                onPressed: () 
                  Navigator.pushNamed(context, '/second_page');
                ,
                child: Text('Goto Second Page'),
              )
            ],
          ),
        );
      ,
    ));
  


class SecondPage extends StatefulWidget 
  @override
  _SecondPageState createState() => _SecondPageState();


class _SecondPageState extends State<SecondPage> 
  @override
  void initState() 
    super.initState();
    BlocProvider.of<TestBloc>(context).add(StateTwoEvent());
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(body: BlocBuilder<TestBloc, TestState>(
      builder: (_, state) 
        String value = '-';
        if (state is StateOne) 
          value = state.value;
         else if (state is StateTwo) 
          value = state.value;
        
        return Center(
          child: Text(value),
        );
      ,
    ));
  

它们都使用相同的 bloc (TestBloc)。在 FirstPage 的初始状态中,我尝试将事件分派给 TestBloc 以将当前状态更改为 StateOne 并在文本中显示当前状态值。所以对于 FirstPage,它将显示 one

之后,我尝试导航到与 FirstPage 几乎相同的 SecondPage。在 SecondPage 的初始状态下,我尝试将事件发送到 TestBloc 以将当前状态更改为 StateTwo,因此它将在文本中显示 two

但问题是当我从 SecondPage 导航.pop 或按下返回按钮时,FirstPage 内的文本变为 two。当我导航到新页面时,似乎 TestBloc 流没有关闭。

我的期望是 FirstPage 应该仍处于显示 one 的最后状态 并且不受我在 SecondPage 中所做的事情的影响。

如何解决这个问题?

谢谢


[更新]

我尝试像这里一样更改 SecondPage,以便它具有另一个 bloc 范围。这实际上解决了问题,但这似乎是正确和好的解决方案?

class SecondPage extends StatefulWidget 
  @override
  _SecondPageState createState() => _SecondPageState();


class _SecondPageState extends State<SecondPage> 
  TestBloc testBloc = TestBloc();
  @override
  void initState() 
    super.initState();
    testBloc.add(StateTwoEvent());
  

  @override
  Widget build(BuildContext context) 
    return BlocProvider(
      create: (context) => testBloc,
      child: Scaffold(body: BlocBuilder<TestBloc, TestState>(
        builder: (_, state) 
          String value = '-';
          if (state is StateOne) 
            value = state.value;
           else if (state is StateTwo) 
            value = state.value;
          
          return Center(
            child: Text(value),
          );
        ,
      )),
    );
  

【问题讨论】:

尝试提交您的更新作为响应,以便可以看到它。许多读者(包括我自己!)没有阅读整个问题。 【参考方案1】:

很难说你到底想达到什么目标。

如果您希望两个页面能够在同一数据上具有不同的状态,则可以为每个页面使用不同的 Bloc 实例。

如果您希望两个页面具有相同的 Bloc 但对状态的反应不同,您可以使用 BlocBuilderbuildWhen 方法来控制构建器何时必须重新构建 UI。第一页示例:

BlocBuilder<TestBloc, TestState>(
    buildWhen: (prevState, curState) 
        return curState is StateOne
    
    builder: (_, state) 
        String value = state.value;
        return Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(value),
              RaisedButton(
                onPressed: () 
                  Navigator.pushNamed(context, '/second_page');
                ,
                child: Text('Goto Second Page'),
             )
           ],
        ),
     );
,

但我认为在您的情况下,您希望拥有同一个 Bloc 的单独实例。 buildWhen 主要用于当您只想在需要在 UI 中反映的部分状态更改时重建。

【讨论】:

谢谢你,普埃洛。你已经很有帮助了。但是你也可以分享一下如何实现“distinct Bloc instance”吗? 您需要有两个不同的BlocProviders 为您的页面提供实例,而不是两个都只有一个。【参考方案2】:

您应该创建新的 StateInProgress 那么你的集团应该喜欢

if (event is StateOneEvent) 
  yield StateInProgress();
  //action call
  yield StateOne("one");
 else if (event is StateTwoEvent) 
  yield StateInProgress();
  //action call
  yield StateTwo("two");

【讨论】:

以上是关于导航到新页面时防止颤动块状态更改的主要内容,如果未能解决你的问题,请参考以下文章

Flutter:状态更改后如何导航到新页面?

每次当前选项卡在 TabBar 颤动中更改时,如何将新页面添加到导航器堆栈?

如何在颤动中维护页面的状态

更改页面后复选框列表保持初始状态

颤动底部导航,其中一个页面有选项卡

使用底部导航栏颤动保存 PageView 内每个页面的状态