Flutter响应式编程与状态管理插件flutter_rx_bloc的使用

Flutter响应式编程与状态管理插件flutter_rx_bloc的使用

概述

flutter_rx_bloc 是一个Flutter包,它通过反应式流的力量帮助实现BLoC设计模式。该包旨在与 rx_blocrx_bloc_generator 一起工作。

主要特性

  • 支持多个输出流(状态)每个BloC。
  • 提供多种构建器小部件,如 RxBlocBuilder, RxBlocMultiBuilder2, RxBlocMultiBuilder3 等。
  • 集成了 RxBlocProvider 用于依赖注入。
  • 提供了 RxResultBuilder 来处理 Result 类型的状态。
  • 包含 RxBlocListener 用于响应状态变化执行特定操作。
  • 支持表单字段的构建和更新,如 RxFormFieldBuilderRxTextFormFieldBuilder

Bloc Widgets

RxBlocBuilder

RxBlocBuilder<NewsBlocType, List<News>>(
  state: (bloc) => bloc.states.news,
  builder: (context, state, bloc) {
    // 返回基于状态的Widget
  }
)

RxBlocMultiBuilder2

RxBlocMultiBuilder2<NewsBlocType, List<BreakingNews>, List<News>>(
  state1: (bloc) => bloc.states.breakingNews,
  state2: (bloc) => bloc.states.news,
  builder: (context, breakingNews, news, bloc) {
    // 返回基于多个状态的Widget
  }
)

RxBlocMultiBuilder3

RxBlocMultiBuilder3<NewsBlocType, List<BreakingNews>, List<News>, List<TopStory>>(
  state1: (bloc) => bloc.states.breakingNews,
  state2: (bloc) => bloc.states.news,
  state3: (bloc) => bloc.states.topStories,
  builder: (context, breakingNews, news, topStories, bloc) {
    // 返回基于多个状态的Widget
  }
)

RxResultBuilder

RxResultBuilder<NewsBlocType, List<News>>(
  state: (bloc) => bloc.states.news,
  buildSuccess: (context, data, bloc) {
    // 返回成功时的Widget
  },
  buildLoading: (context, bloc) {
    // 返回加载中的Widget
  },
  buildError: (context, error, bloc) {
    // 返回错误时的Widget
  },
)

RxBlocProvider

RxBlocProvider<BlocAType>(
  create: (BuildContext context) => BlocA(),
  child: ChildA(),
);

RxMultiBlocProvider

RxMultiBlocProvider(
  providers: [
    RxBlocProvider<BlocAType>(
      create: (BuildContext context) => BlocA(),
    ),
    RxBlocProvider<BlocBType>(
      create: (BuildContext context) => BlocB(),
    ),
    RxBlocProvider<BlocCType>(
      create: (BuildContext context) => BlocC(),
    ),
  ],
  child: ChildA(),
)

RxBlocListener

RxBlocListener<NewsBlocType, bool>(
  state: (bloc) => bloc.states.isLoading,
  listener: (context, state) {
    // 基于状态执行某些操作
  }
)

示例代码

CounterApp

import 'package:flutter/material.dart';
import 'package:flutter_rx_bloc/flutter_rx_bloc.dart';
import 'package:rx_bloc/rx_bloc.dart';
import 'package:rxdart/rxdart.dart';

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

class CounterApp extends StatelessWidget {
  const CounterApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => MaterialApp(
        title: 'Counter sample',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: RxBlocProvider<CounterBlocType>(
          create: (ctx) => CounterBloc(CounterRepository()),
          child: const CounterPage(),
        ),
      );
}

class CounterPage extends StatelessWidget {
  const CounterPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) => Scaffold(
        appBar: AppBar(title: const Text('Counter sample')),
        body: Center(
          child: RxBlocListener<CounterBlocType, String>(
            state: (bloc) => bloc.states.errors,
            listener: _showError,
            child: RxBlocBuilder<CounterBlocType, int>(
              state: (bloc) => bloc.states.count,
              builder: (context, snapshot, bloc) =>
                  _buildCount(snapshot, context),
            ),
          ),
        ),
        floatingActionButton: RxLoadingBuilder<CounterBlocType>(
          state: (bloc) => bloc.states.isLoading,
          builder: (context, isLoading, tag, bloc) => Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              ActionButton(
                tooltip: 'Increment',
                iconData: Icons.add,
                onPressed: bloc.events.increment,
                disabled: isLoading,
                loading: isLoading && tag == CounterBloc.tagIncrement,
              ),
              const SizedBox(width: 16),
              ActionButton(
                tooltip: 'Decrement',
                iconData: Icons.remove,
                onPressed: bloc.events.decrement,
                disabled: isLoading,
                loading: isLoading && tag == CounterBloc.tagDecrement,
              ),
            ],
          ),
        ),
      );

  StatelessWidget _buildCount(
    AsyncSnapshot<int> snapshot,
    BuildContext context,
  ) =>
      snapshot.hasData
          ? Text(
              snapshot.data.toString(),
              style: Theme.of(context).textTheme.headlineMedium,
            )
          : Container();

  void _showError(BuildContext context, String errorMessage) =>
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text(errorMessage),
          behavior: SnackBarBehavior.floating,
        ),
      );
}

class ActionButton extends StatelessWidget {
  const ActionButton({
    required this.iconData,
    required this.onPressed,
    this.disabled = false,
    this.tooltip = '',
    this.loading = false,
    Key? key,
  }) : super(key: key);

  final bool disabled;
  final bool loading;
  final String tooltip;
  final IconData iconData;
  final VoidCallback onPressed;

  @override
  Widget build(BuildContext context) {
    if (loading) {
      return const Padding(
        padding: EdgeInsets.symmetric(horizontal: 10),
        child: CircularProgressIndicator(),
      );
    }

    return FloatingActionButton(
      backgroundColor: disabled ? Colors.blueGrey : Colors.blue,
      onPressed: !disabled ? onPressed : null,
      tooltip: tooltip,
      child: Icon(iconData),
    );
  }
}

extension AsyncSnapshotLoadingState on AsyncSnapshot<bool> {
  bool get isLoading => hasData && data!;
  Color get buttonColor => isLoading ? Colors.blueGrey : Colors.blue;
}

CounterBloc

abstract class CounterBlocEvents {
  void increment();
  void decrement();
}

abstract class CounterBlocStates {
  Stream<int> get count;
  Stream<LoadingWithTag> get isLoading;
  Stream<String> get errors;
}

@RxBloc()
class CounterBloc extends $CounterBloc {
  CounterBloc(this._repository);

  final CounterRepository _repository;

  static const tagIncrement = 'Increment';
  static const tagDecrement = 'Decrement';

  @override
  Stream<int> _mapToCountState() => Rx.merge<Result<int>>([
        _$incrementEvent.switchMap(
            (_) => _repository.increment().asResultStream(tag: tagIncrement)),
        _$decrementEvent.switchMap(
            (_) => _repository.decrement().asResultStream(tag: tagDecrement)),
      ])
          .setResultStateHandler(this)
          .whereSuccess()
          .startWith(0);

  @override
  Stream<String> _mapToErrorsState() =>
      errorState.map((error) => error.toString());

  @override
  Stream<LoadingWithTag> _mapToIsLoadingState() => loadingWithTagState;
}

CounterRepository

class CounterRepository {
  int _counter = 0;

  Future<int> increment() async {
    await Future.delayed(const Duration(milliseconds: 800));
    if (_counter == 2) {
      throw Exception('Maximum number is reached!');
    }
    return ++_counter;
  }

  Future<int> decrement() async {
    await Future.delayed(const Duration(milliseconds: 800));
    if (_counter <= 0) {
      throw Exception('Minimum number is reached!');
    }
    return --_counter;
  }
}

通过上述代码示例,可以清晰地看到如何在Flutter中使用 flutter_rx_bloc 实现响应式编程和状态管理。希望这些内容对你有所帮助!


更多关于Flutter响应式编程与状态管理插件flutter_rx_bloc的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter响应式编程与状态管理插件flutter_rx_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,响应式编程与状态管理是两个核心概念,特别是在构建复杂应用时。flutter_rx_bloc 是一个结合了 Dart 的响应式扩展(rxdart)和 Bloc 状态管理模式的库,它使得在 Flutter 应用中实现响应式逻辑更加简洁和高效。

下面是一个简单的示例,展示如何在 Flutter 应用中使用 flutter_rx_bloc 进行响应式编程和状态管理。

1. 添加依赖

首先,在你的 pubspec.yaml 文件中添加 flutter_blocrxdart 的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.0.0
  rxdart: ^0.27.0

2. 创建 Cubit 和 State

定义一个简单的 Cubit 和它的状态。在这个例子中,我们将创建一个计数器 Cubit。

import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';

class CounterState {
  final int count;

  CounterState({required this.count});

  @override
  String toString() => 'CounterState{count: $count}';
}

class CounterCubit extends Cubit<CounterState> {
  CounterCubit() : super(CounterState(count: 0));

  void increment() => emit(CounterState(count: state.count + 1));

  void decrement() => emit(CounterState(count: state.count - 1));
}

3. 创建响应式 BLoC

使用 rxdart 创建一个响应式的 BLoC。在这个例子中,我们将创建一个监听计数器变化的 BLoC。

import 'package:bloc/bloc.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:rxdart/rxdart.dart';
import 'counter_cubit.dart';

class ResponsiveCounterBloc extends Bloc<void, int> {
  final CounterCubit counterCubit;

  ResponsiveCounterBloc({required this.counterCubit})
      : super(counterCubit.state.count) {
    // Listen to counterCubit's state changes
    _subscribeToCounter();
  }

  void _subscribeToCounter() {
    counterCubit.stream.map((state) => state.count).listen((count) {
      add(count); // Emit the new count to this bloc's state
    });
  }

  @override
  Stream<int> mapEventToState(void event) async* {
    // In this case, the event is always the new count from counterCubit
    yield event as int;
  }

  @override
  void onClose() {
    // Clean up subscriptions when the bloc is closed
    super.onClose();
  }
}

4. 使用 BLoC 和 Cubit 在 Flutter UI 中

在你的 Flutter 应用中使用这些 BLoC 和 Cubit。

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_cubit.dart';
import 'responsive_counter_bloc.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MultiBlocProvider(
        providers: [
          BlocProvider<CounterCubit>(
            create: (_) => CounterCubit(),
          ),
          BlocProvider<ResponsiveCounterBloc>(
            create: (_) => ResponsiveCounterBloc(counterCubit: context.read<CounterCubit>()),
          ),
        ],
        child: MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final CounterCubit counterCubit = context.read<CounterCubit>();
    final ResponsiveCounterBloc responsiveCounterBloc = context.read<ResponsiveCounterBloc>();

    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Responsive Programming with flutter_rx_bloc'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            BlocBuilder<ResponsiveCounterBloc, int>(
              builder: (context, count) {
                return Text(
                  '$count',
                  style: Theme.of(context).textTheme.headline4,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: () => counterCubit.decrement(),
            tooltip: 'Decrement',
            child: Icon(Icons.remove),
          ),
          SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () => counterCubit.increment(),
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        ],
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的计数器应用,使用 CounterCubit 管理计数器的状态,并通过 ResponsiveCounterBloc 监听 CounterCubit 的状态变化,然后在 UI 中显示当前的计数值。

注意:虽然 flutter_rx_bloc 并不是 Flutter 官方或 Bloc 库的直接部分,但上述示例展示了如何使用 rxdart 与 Bloc 结合来实现响应式状态管理。如果你确实想使用 flutter_rx_bloc 特定的功能,你可能需要查阅该库的文档来获取更多详细信息和特定用法。

回到顶部