颤动变化通知

Flutter change notifier

您好,我对 flutter 很陌生,发现很难使用 websocket 触发器管理状态。我已经能够在微控制器和 flutter 之间建立 websocket 连接。所以我一直在尝试做的是,当某条消息以 flutter 的形式到达时触发一个事件。为此,我建立了一个带有 ChangeNotifier 扩展的模型。我已经使用 context.watch() 来跟踪更改。当我从正在监视数据的构建函数中手动更改数据时,它会工作并且小部件树会重建,但是当数据在构建函数之外被操作时,例如通过 websocket 事件,小部件不会重建。我还有一个基于连续计时器的设置状态重建,每分钟都会触发一次。每次小部件重建时,我都可以看到我的模型 class 中存在的数据与每个 Websocket 事件一起正确存储,但它没有触发重建。我不明白我做错了什么。我的代码分布在多个文件中,所以我现在可以展示它。

我已尽我所能解释问题,如果您需要更多解释,请告诉我。我好几天都在寻找答案:-(

EDIT:3(在实际代码中有另一个页面用于调用 ClockSettingPage 它是 bottomTabBarPage)我更新了下面的代码以更好地表示实际代码。

编辑 4:已解决 :-)

// MAIN
void main() => runApp(
    //USED MULTI PROVIDE BECAUSE THERE ARE MORE THAN ONE IN ACTUAL CODE//
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => WebsocketsProvider()),
        ],
        child: MyApp(),
      ),
    );


class MyApp extends StatelessWidget {
  static const MaterialColor white = const MaterialColor(

  @override
  Widget build(BuildContext context) {
  return MaterialApp(
    routes: {
      TabsScreenPage.routeName: (context) => TabsScreenPage(),
      ClockSettingsPage.routeName: (context) => ClockSettingsPage(),
      },
    );
  }
}



// MY MODEL
class WebsocketsProvider extends ChangeNotifier {

  static final IOWebSocketChannel channel =
      new IOWebSocketChannel.connect('ws://192.168.1.17:81/');

  int _wsEventTrack;
  int get wsEventTrack => _wsEventTrack;
  set wsEventTrack(value) => _wsEventTrack = value;

  void incrementEvent()
  {
    wsEventTrack = wsEventTrack + 1;
    notifyListeners();    // IMPORTANT
  }

  void subscribeToStream() 
  {
  StreamSubscription subscription = channel.stream.listen(
  (payload) {
    Map tempResponse =
      jsonDecode(payload.toString()) as Map<Object, Object>;
    if (tempResponse['echo'] == 'echo') {

      //ws connected and right data received
      print('triggered');
      incrementEvent();
    }
  }
}


///////////////////////
//tab screen page
///////////////////////

class TabsScreenPage extends StatefulWidget {
  static const routeName = '/tabsScreenPage';

  @override
  _TabsScreenPageState createState() => _TabsScreenPageState();
}

class _TabsScreenPageState extends State<TabsScreenPage> {

/* HERE I ACTUALLY SUBSCRIBE TO THE WEBSOCKET STREAM */
WebsocketsProvider().subscribeToStream();

void _selectPage(int index) {
    
    setState(() {
      _selectedPageIndes = index;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(

        body: _pages[_selectedPageIndes]['page'],
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          currentIndex: _selectedPageIndes,
          items: [
            BottomNavigationBarItem(
                icon: Icon(Icons.access_time),
            BottomNavigationBarItem(
                icon: Icon(Icons.settings),
            BottomNavigationBarItem(
                icon: Icon(Icons.calendar_today),
          ],
          onTap: _selectPage,
        ),
      ),
    );
  }
}

///////////////////////////////////////////////////
///  Class where the data is processed and required
// this needs to rebuild when there is a change in the
//  WEBSOCKETPROVIDER CLASS's DATA
///////////////////////////////////////////////////

class ClockSettingsPage extends StatefulWidget {
  static const routeName = '/clockSettingsPage';
  @override
  _ClockSettingsPageState createState() => _ClockSettingsPageState();
}

class _ClockSettingsPageState extends State<ClockSettingsPage> {
  
  // SETUP OF THE TIMER REBUILDS IS IN THE INIT STATE//

  @override
  Widget build(BuildContext context) {
    
    final notifier = context.watch<WebsocketsProvider>();
  return Scaffold(
    body: Container(
      child: InkWell(
        /* THE PROBLEM LIES HERE WHEN THE INKWELL IS TAPPED 
THE DATA INSIDE THE WEBSOCKETS PROVIDER CLASS IS INCREMENTED
 AND THE WIDGET GETS REBUILD BUT WHEN THE DATA IS INCREMENTED
 IN THE WEBSOCKET PROVIDER CLASS INTERNALLY DUE TO WEBSOCKET
 STREAM THIS WIDGET DOES NOT GETS REBUILD ALTHOUGH I KNOW THAT
 THE DATA IS BEING STORE WITH THE WEBSOCKET EVENTS BECAUSE IN
 THE INIT OF THIS CLASS THERE IS A CONTINUOUS REBUILD EVERY
 MINUTE AND WHENEVER THE WIDGET REBUILDS THE MISSING DATA GETS
 CORRECTED */
            onTap: notifier.incrementEvent,
            child: Text(notifier.wsEventTrack.toString()),
            ),
          ),
        );
      }
    }

问题

对于 ChangeNotifier,您需要确保您引用的是您正在更新和收听的 class 的同一个实例。

到目前为止一切顺利

您正在 MultiProvider 小部件中创建 WebsocketsProvider class(扩展 ChangeNofitier)的实例并将其作为 ChangeNotifierProvider 提供.然后,您将在 ClockSettingsPage 中收听该实例。到目前为止一切都是正确的。

不匹配

在您的 _TabsScreenPageState 中,您正在创建 WebsocketsProvider class 的全新实例并收听该流。这不起作用,因为 ClockSettingsPage 正在监听 class.

的完全不同实例的更改

更新

initState 方法覆盖到您的 _TabsScreenPageState 并像往常一样调用 super。然后,像这样订阅 Provider 创建的 class 的实例:

context.read<WebsocketsProvider>().subscribeToStream();

如果上面的语法不起作用,你可以用老式的方法来做:

Provider.of<WebsocketsProvider>(context, listen: false).subscribeToStream();

p.s。我没有看到你在任何地方处理你的流。如果您不这样做,请查看如何在 Dart 中处理流。这将防止您的应用程序发生内存泄漏。