Flutter状态管理副作用处理插件flutter_bloc_side_effect的使用

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

Flutter状态管理副作用处理插件flutter_bloc_side_effect的使用

标题

Flutter状态管理副作用处理插件flutter_bloc_side_effect的使用

内容

  • 动机

    • 这个包旨在解决在使用BLoC状态管理开发移动应用时可能遇到的一些问题。这些问题包括: 1 将许多参数放在状态类中只是为了在UI上响应它们。现在可以通过使用单独的状态流来摆脱这一点。 2 发出状态只是因为需要显示toast或执行任何小动作在UI上,即使你的业务逻辑不暗示这种发出。 3 必须连续发出两种不同的状态以在UI上执行某些操作,因为flutter_bloc当前实现故意不监听状态是否为同一类型。
  • 副作用特性

    • 副作用特性受到side_effect_bloc启发,并且基于原始flutter_bloc的一些新实用工具。 使用它们就像通常一样,但需要提供一个副作用处理器。这些组件如下: 1 BlocBuilder → BlocBuilderWithSideEffects 2 BlocListener → BlocListenerWithSideEffects 3 BlocConsumer → BlocConsumerWithSideEffects。
  • 使用说明

    • 如果你不需要副作用特性,请使用原生组件在UI上。 但现在必须提供bloc参数,因为它被要求。 这是唯一的区别。 这些组件是: 1 BlocBuilder 2 BlocListener 3 BlocConsumer。
  • 注意

    • 一些原始类在这个包中没有出现!这些是: 1 DI相关的(BlocProvider, MultiBlocProvider, RepositoryProvider, MultiRepositoryProvider) 2 Cubit 3 其他(MultiBlocListener, BlocSelector)。

示例代码

import 'package:flutter/material.dart';
import 'package:flutter_bloc_side_effect/flutter_bloc_side_effect.dart';
import 'package:flutter_bloc_side_effect_example/my_bloc.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(home: MyView());
  }
}

final myBloc = MyBloc();

class MyView extends StatelessWidget {
  const MyView({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return BlocConsumerWithSideEffects<MyBloc, MyState, MySideEffect>(
      bloc: myBloc,
      sideEffectsListener: (context, sideEffect) {
        sideEffect.map(
          error: (sideEffect) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text(sideEffect.message)),
            );
          },
        );
      },
      listener: (_, state) => log(state.stateName),
      builder: (context, state) {
        return Scaffold(
          appBar: AppBar(title: Text(state.stateName)),
          floatingActionButton: Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              FloatingActionButton(
                onPressed: () => myBloc.add(const MyEvent.throwError()),
                child: const Text('Error'),
              ),
            ],
          ),
        );
      },
    );
  }
}

完整示例demo

import 'package:flutter/material.dart';
import 'package:flutter_bloc_side_effect/flutter_bloc_side_effect.dart';
import 'package:flutter_bloc_side_effect_example/my_bloc.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(home: MyView());
  }
}

final myBloc = MyBloc();

class MyView extends StatelessWidget {
  const MyView({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return BlocConsumerWithSideEffects<MyBloc, MyState, MySideEffect>(
      bloc: myBloc,
      sideEffectsListener: (context, sideEffect) {
        sideEffect.map(
          error: ( sideEffect) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text(sideEffect.message)),
            );
          },
        );
      },
      listener: (_, state) => log(state.stateName),
      builder: (context, state) {
        return Scaffold(
          appBar: AppBar(title: Text(state.stateName)),
          floatingActionButton: Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              FloatingActionButton(
                onPressed: () => myBloc.add(const MyEvent.throwError()),
                child: const Text('Error'),
              ),
            ],
          ),
        );
      },
    );
  }
}

更多关于Flutter状态管理副作用处理插件flutter_bloc_side_effect的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter状态管理副作用处理插件flutter_bloc_side_effect的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在处理Flutter应用中的状态管理及其副作用时,flutter_bloc 是一个非常流行的库,而 flutter_bloc_side_effect 则是其一个有用的扩展,专门用于处理BLoC(Business Logic Component)中的副作用(如API调用、日志记录、推送通知等)。下面是一个关于如何使用 flutter_bloc_side_effect 的代码示例,演示了如何在BLoC中处理副作用。

1. 添加依赖

首先,确保在你的 pubspec.yaml 文件中添加了 flutter_blocflutter_bloc_side_effect 的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.0.0  # 请检查最新版本号
  flutter_bloc_side_effect: ^0.1.0  # 请检查最新版本号

2. 创建BLoC和状态

接下来,创建一个简单的BLoC和相关的状态类。例如,我们有一个用于管理用户登录的BLoC。

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_bloc_side_effect/flutter_bloc_side_effect.dart';

// 定义状态
enum LoginStatus { loading, success, failure }

class LoginState {
  final LoginStatus status;
  final String? message;

  LoginState(this.status, this.message);

  // 工厂方法创建不同的状态实例
  factory LoginState.loading() => LoginState(LoginStatus.loading, null);
  factory LoginState.success() => LoginState(LoginStatus.success, "Login successful");
  factory LoginState.failure(String message) => LoginState(LoginStatus.failure, message);
}

// 定义事件
class LoginEvent extends Equatable {
  final String username;
  final String password;

  LoginEvent(this.username, this.password);

  @override
  List<Object?> get props => [username, password];
}

// 创建BLoC
class LoginBloc extends Bloc<LoginEvent, LoginState> with BlocSideEffect<LoginEvent, LoginState> {
  LoginBloc() : super(LoginState.loading()) {
    on<LoginEvent>((event, emit) async {
      emit(state.copyWith(status: LoginStatus.loading));

      // 模拟登录API调用
      bool isAuthenticated = await authenticateUser(event.username, event.password);

      if (isAuthenticated) {
        emit(state.copyWith(status: LoginStatus.success));
      } else {
        emit(state.copyWith(status: LoginStatus.failure, message: "Invalid credentials"));
      }

      // 添加副作用处理
      addSideEffect(effect: (state) async {
        if (state.status == LoginStatus.success) {
          // 例如,记录登录成功日志
          await logSuccess("User ${event.username} logged in successfully");
        } else if (state.status == LoginStatus.failure) {
          // 例如,发送错误通知
          await showErrorNotification("Login failed: ${state.message ?? "Unknown error"}");
        }
      });
    });
  }

  // 模拟的用户认证函数
  Future<bool> authenticateUser(String username, String password) async {
    // 这里可以添加实际的API调用逻辑
    await Future.delayed(Duration(seconds: 2));
    return username == "user" && password == "pass";
  }

  // 模拟的日志记录函数
  Future<void> logSuccess(String message) async {
    // 这里可以添加实际的日志记录逻辑
    print(message);
  }

  // 模拟的错误通知函数
  Future<void> showErrorNotification(String message) async {
    // 这里可以添加实际的通知显示逻辑
    print("Notification: $message");
  }
}

3. 使用BLoC在UI中

最后,在你的Flutter组件中使用这个BLoC来管理UI状态。

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'login_bloc.dart';  // 假设上面的代码保存在这个文件中

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider(
        create: (context) => LoginBloc(),
        child: LoginScreen(),
      ),
    );
  }
}

class LoginScreen extends StatelessWidget {
  final TextEditingController _usernameController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Login")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _usernameController,
              decoration: InputDecoration(labelText: "Username"),
            ),
            TextField(
              controller: _passwordController,
              decoration: InputDecoration(labelText: "Password"),
              obscureText: true,
            ),
            SizedBox(height: 20),
            BlocButton<LoginBloc, LoginState>(
              buildWhen: (previous, current) => current.status == LoginStatus.loading || current.status == LoginStatus.failure,
              onPressed: () {
                context.read<LoginBloc>().add(LoginEvent(_usernameController.text, _passwordController.text));
              },
              child: Text("Login"),
            ),
            if (context.select<LoginBloc, LoginStatus>((bloc) => bloc.state.status) == LoginStatus.success)
              Text("Login successful!"),
            if (context.select<LoginBloc, LoginStatus>((bloc) => bloc.state.status) == LoginStatus.failure)
              Text("${context.select<LoginBloc, String?>((bloc) => bloc.state.message ?? "")}"),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个 LoginBloc,它处理登录事件,并在状态改变时触发副作用(如日志记录和通知显示)。我们还创建了一个简单的UI来演示如何使用这个BLoC。注意,这个示例中的 authenticateUserlogSuccessshowErrorNotification 函数都是模拟的,你需要根据你的应用需求来实现这些逻辑。

回到顶部