Flutter数据流监听插件stream_listener_widget的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter数据流监听插件stream_listener_widget的使用

stream_listener_widget 是一个简单的Widget,用于监听Stream而不重建其子组件(例如显示对话框)。

简单保持

  • 逻辑类(ViewModel、Controller等)不应该使用BuildContext。
  • 触发弹出窗口或导航属于视图(View)的责任。

StreamListener Widget与Flutter的StreamBuilder Widget一样简单。

  • 它允许你响应Stream事件而无需重建任何Widget。
  • 它会为你清理内存,取消所有给定Stream的订阅。

以下是一个例子,我们的逻辑类仅触发一些事件,这些事件由视图处理以进行导航或显示错误对话框。

class MyView extends StatelessWidget {
  final MyController logic;

  const MyView({super.key, required this.logic});

  void _onLoginSuccess(LoginSuccess e) {
    Navigator.of(context).pushNamed('/homePage');
  }

  void _onError(LoginError e) {
    showDialog(
      context: context,
      barrierDismissible: true,
      builder: (context) => Dialog(child: Text('An unknown error occurred')),
    );
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return StreamListener(
      listeners: [
        (context) => logic.controller.stream.whereType<LoginSuccess>().listen(_onLoginSuccess),
        (context) => logic.controller.stream.whereType<LoginError>().listen(_onError),
      ],
      child: Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () => logic.submitForm('my_email', 'my_password'),
          child: const Text('Submit'),
        ),
      ),
    );
  }
}

/// 这里定义了一些类型类来表示可能的领域/逻辑事件
sealed class MyEvent {}
class LoginSuccess extends MyEvent {}
class LoginError extends MyEvent {}
  • 我们的视图处理视图逻辑。
  • 我们的逻辑类处理业务逻辑。
  • 责任和依赖关系被很好地分离。
  • 我们的控制器进行了领域驱动设计,可以用于其他地方。
  • 通过定义一个类型的逻辑事件,业务逻辑更加清晰。
  • 代码稳定且可测试。

以下是你应该避免的做法(在逻辑类中处理导航、弹窗等):

class MyController {
  BuildContext context;

  Future<void> submitForm(String email, String password) async {
    try {
      await authenticationApi.login(email, password);
      Navigator.of(context).pushNamed('/homePage');
    } catch (e) {
      showDialog(
        context: context,
        barrierDismissible: true,
        builder: (context) => Dialog(child: Text('An unknown error occurred')),
      );
    }
  }
}

class MyView extends StatefulWidget {
  final logic = MyController();

  const MyView({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    logic.context = context; // 这真的很难看
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () => logic.submitForm('my_email', 'my_password'),
        child: const Text('Submit'),
      ),
    );
  }
}

但是等等?上面的例子更短!

  • 是的!但我们没有将视图逻辑和业务逻辑分开。
  • 我们的控制器做了一切,而视图什么也不做。
  • 责任和依赖关系混在一起了。
  • 你的控制器是为特定的视图设计的。
  • BuildContext在Future/异步间隔后被使用,这是禁止的,因为它可能是不稳定的。

示例代码

main.dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:stream_listener_widget/stream_listener_widget.dart';

void main() => runApp(const MaterialApp(home: CounterView()));

class CounterView extends StatefulWidget {
  const CounterView({super.key});

  [@override](/user/override)
  State<CounterView> createState() => _CounterViewState();
}

class _CounterViewState extends State<CounterView> {
  final logic = CounterLogic();

  void _onMaxReached(int state) {
    showDialog(
      context: context,
      barrierDismissible: true,
      builder: (context) {
        return Dialog(
          child: Padding(
            padding: const EdgeInsets.all(20),
            child: Text('Your counter: $state has reached or exceeded max'),
          ),
        );
      },
    );
  }

  [@override](/user/override)
  void dispose() {
    logic.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return StreamListener(
      listeners: [
        (context) => logic.maxReached.listen(_onMaxReached),
      ],
      child: Scaffold(
        appBar: AppBar(title: const Text('Counter demo')),
        body: Center(
          child: StreamBuilder(
            initialData: logic.state,
            stream: logic.controller.stream,
            builder: (context, snapshot) => Text('Counter: ${snapshot.data}'),
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: logic.increment,
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

class CounterLogic {
  int state = 0;
  final controller = StreamController<int>.broadcast();

  Stream<int> get maxReached => controller.stream.where((e) => e >= 5);

  void increment() => controller.add(++state);

  void dispose() => controller.close();
}

更多关于Flutter数据流监听插件stream_listener_widget的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据流监听插件stream_listener_widget的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用stream_listener_widget插件进行数据流监听的代码示例。stream_listener_widget插件是一个方便的工具,用于简化对Dart Streams的监听和处理。虽然这不是一个官方的Flutter插件,但假设它提供了类似的功能,我们可以编写一个示例来展示其基本用法。

首先,确保你已经在pubspec.yaml文件中添加了stream_listener_widget依赖(如果这是一个真实存在的插件):

dependencies:
  flutter:
    sdk: flutter
  stream_listener_widget: ^x.y.z  # 替换为实际版本号

然后,运行flutter pub get来获取依赖。

接下来,我们可以编写一个示例应用,展示如何使用这个插件来监听一个Stream。

示例代码

import 'package:flutter/material.dart';
import 'package:stream_listener_widget/stream_listener_widget.dart'; // 假设插件提供这个导入路径
import 'dart:async';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Stream Listener Widget Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: StreamListenerDemo(),
    );
  }
}

class StreamListenerDemo extends StatefulWidget {
  @override
  _StreamListenerDemoState createState() => _StreamListenerDemoState();
}

class _StreamListenerDemoState extends State<StreamListenerDemo> {
  final StreamController<String> _controller = StreamController<String>();

  @override
  void dispose() {
    _controller.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Stream Listener Widget Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            StreamListenerWidget<String>(
              stream: _controller.stream,
              onData: (data) {
                return Text(
                  'Stream Data: $data',
                  style: TextStyle(fontSize: 24),
                );
              },
              onError: (error) {
                return Text(
                  'Stream Error: $error',
                  style: TextStyle(fontSize: 24, color: Colors.red),
                );
              },
              onDone: () {
                return Text(
                  'Stream Done',
                  style: TextStyle(fontSize: 24),
                );
              },
              loading: CircularProgressIndicator(),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                _controller.add('Button pressed');
              },
              child: Text('Press Me'),
            ),
          ],
        ),
      ),
    );
  }
}

解释

  1. 依赖导入:假设stream_listener_widget插件提供了一个简单的导入路径。
  2. Stream 控制:我们使用StreamController来创建一个Stream,用于发送数据事件。
  3. StreamListenerWidget:这是我们的核心组件,用于监听Stream并显示数据。
    • stream:指定要监听的Stream。
    • onData:当Stream发送数据时调用的构建函数。
    • onError:当Stream发生错误时调用的构建函数。
    • onDone:当Stream结束时调用的构建函数。
    • loading:在Stream开始但尚未发送数据时显示的Widget。
  4. 按钮:一个按钮,每次按下时都会向Stream发送一个数据事件。

请注意,这个示例代码是基于假设stream_listener_widget插件存在的。如果这是一个虚构的插件或者你有具体的插件实现细节,请根据实际情况调整代码。如果插件不存在,你可以考虑使用Flutter内置的StreamBuilder来实现类似的功能。

回到顶部