Flutter状态管理插件ajwah_bloc的使用

Flutter状态管理插件ajwah_bloc的使用

ajwah_bloc

ajwah_bloc 是一个用于实现 BLoC 模式的 Dart 包。每个 StateController 都具有以下功能:

  • 分发操作
  • 过滤操作
  • 添加效果
  • 控制器之间的通信
  • 完整的 RxDart 功能

请查看 示例 。示例包含 countertodos 页面,这些页面演示了所有功能。

CounterState

import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'package:example/widgets/StreamConsumer.dart';
import 'package:rxdart/rxdart.dart';

class CounterState extends StateController<int> {
  CounterState() : super(0);

  @override
  void onInit() {
    // 当接收到 'asyncInc' 类型的操作时,延迟 1 秒后将状态增加 1
    mapActionToState([
      action$
          .whereType('asyncInc')
          .delay(const Duration(seconds: 1))
          .map((event) => state + 1),
    ]);
  }

  // 增加状态值
  void inc() => emit(state + 1);

  // 减少状态值
  void dec() => emit(state - 1);

  // 返回当前状态值
  Stream<SCResponse> get count$ =>
      Rx.merge([
        action$.whereType('asyncInc').mapTo(SCLoading()),
        stream$.map((data) => data > 10
            ? SCError('Counter is out of the range.')
            : SCData('$data')),
      ]);
}

TodoState

import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'package:rxdart/rxdart.dart';

import '../api/todoApi.dart';
import './searchCategory.dart';

class TodoState extends StateController<List<Todo>> {
  TodoState() : super([]);

  @override
  void onInit() {
    loadTodos();
    /**
     * 对于 todo 搜索输入的效果。每次按键都会从 AddTodo 小部件分发 SearchInputAction。
     * 但效果会将其节流到 320 毫秒以收集后续操作,并最终分发 SearchTodoAction。
     */
    registerEffects([
      action$
          .isA<SearchInputAction>()
          .debounceTime(const Duration(milliseconds: 320))
          .map((action) => SearchTodoAction(action.searchText))
    ]);
  }

  // 加载待办事项
  void loadTodos() {
    getTodos().listen((todos) {
      emit(todos);
    });
  }

  // 添加新的待办事项
  void add(String description) {
    addTodo(Todo(description: description))
        .listen((todo) => emit([...state, todo]));
  }

  // 更新待办事项
  void update(Todo todo) {
    updateTodo(todo).listen(
        (todo) => emit([
              for (var item in state)
                if (item.id == todo.id) todo else item,
            ]), onError: (error) {
      dispatch(TodoErrorAction(error));
    });
  }

  // 删除待办事项
  void remove(Todo todo) {
    removeTodo(todo).listen(
        (todo) => emit(state.where((item) => item.id != todo.id).toList()));
  }

  // 获取活动待办事项信息
  Stream<String> get activeTodosInfo$ =>
      stream$
          .map((todos) => todos.where((todo) => !todo.completed).toList())
          .map((todos) => '${todos.length} items left');

  // 结合多个控制器(TodoState, SearchCategoryState)并返回单个待办事项流
  Stream<List<Todo>> get todo$ =>
      Rx.combineLatest3<List<Todo>, SearchCategory, String, List<Todo>>(
          stream$,
          remoteStream<SearchCategoryState, SearchCategory>(),
          action$
              .isA<SearchTodoAction>()
              .map<String>((action) => action.searchText)
              .doOnData((event) {
            print('searchText: ' + event);
          }).startWith(''), (todos, category, searchText) {
        if (searchText.isNotEmpty)
          todos = todos
              .where((todo) => todo.description
                  .toLowerCase()
                  .contains(searchText.toLowerCase()))
              .toList();
        switch (category) {
          case SearchCategory.Active:
            return todos.where((todo) => !todo.completed).toList();
          case SearchCategory.Completed:
            return todos.where((todo) => todo.completed).toList();
          default:
            return todos;
        }
      });
}

// 待办事项错误动作
class TodoErrorAction extends Action {
  final dynamic error;
  TodoErrorAction(this.error);
}

// 搜索待办事项动作
class SearchTodoAction extends Action {
  final String searchText;
  SearchTodoAction(this.searchText);
}

// 搜索输入动作
class SearchInputAction extends Action {
  final String searchText;
  SearchInputAction(this.searchText);
}

SearchCategoryState

import 'package:ajwah_bloc/ajwah_bloc.dart';

enum SearchCategory { All, Active, Completed }

class SearchCategoryState extends StateController<SearchCategory> {
  SearchCategoryState() : super(SearchCategory.All);

  // 设置搜索类别
  void setCategory(SearchCategory category) => emit(category);
}

Consuming State

final controller = CounterStateController();

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

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment.center,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            child: Text('inc'),
            onPressed: controller.inc,
          ),
          ElevatedButton(
            child: Text('dec'),
            onPressed: controller.dec,
          ),
          ElevatedButton(
            child: Text('async(+)'),
            onPressed: controller.asyncInc,
          ),
          StreamBuilder(
            stream: controller.count$,
            initialData: '',
            builder: (context, snapshot) => Text(snapshot.data.toString())
          ),
        ],
      ),
    );
  }
}

Testing

void main() {
  CounterStateController? controller;
  setUp(() {
    controller = CounterStateController();
  });

  tearDown(() {
    controller?.dispose();
  });

  ajwahTest<int>(
    'Initial state',
    build: () => controller!.stream$,
    expect: [isA<int>()],
    verify: (state) {
      expect(state[0], 0);
    },
  );

  ajwahTest<int>(
    'increment',
    build: () => controller!.stream$,
    act: () => controller?.increment(),
    skip: 1,
    expect: [isA<int>()],
    verify: (state) {
      expect(state[0], 1);
    },
  );

  ajwahTest<int>(
    'decrement',
    build: () => controller!.stream$,
    act: () => controller?.decrement(),
    skip: 1,
    expect: [isA<int>()],
    verify: (state) {
      expect(state[0], -1);
    },
  );
}

API

  Actions get action$  // 获取操作流
  void dispatch(Action action)  // 分发操作
  void onAction(Action action)  // 处理操作
  void onInit()  // 初始化方法
  S get state  // 获取当前状态
  Stream<S> get stream$  // 获取状态流
  Stream<T> select<T>(T Function(S state) mapCallback)  // 选择状态
  void emit(S newState)  // 发射新状态
  void registerEffects(Iterable<Stream<Action>> callbackList)  // 注册效果
  void importState(S state)  // 导入状态
  Stream<Controller> remoteController<Controller>()  // 远程控制器
  Future<State> remoteState<Controller, State>()  // 远程状态
  Stream<S> remoteStream<Controller, S>()  // 远程状态流
  void mapActionToState(Iterable<Stream<S>> callbackList)  // 映射操作到状态
  void dispose()  // 销毁

示例代码

import 'package:example/states/counter.dart';
import 'package:flutter/material.dart';
import 'package:get/instance_manager.dart';
import 'package:get/route_manager.dart';

import './states/todo.dart';
import './pages/counterPage.dart';
import './pages/todoPage.dart';
import './states/searchCategory.dart';

void main() {
  runApp(GetMaterialApp(
    smartManagement: SmartManagement.keepFactory,
    debugShowCheckedModeBanner: false,
    getPages: [
      GetPage(
          name: '',
          page: () => CounterPage(),
          binding: BindingsBuilder(() {
            Get.lazyPut(() => CounterState());
          })),
      GetPage(
          name: '/todo',
          page: () => TodoPage(),
          transition: Transition.zoom,
          binding: BindingsBuilder(() {
            Get.put<TodoState>(TodoState(), permanent: true);
            Get.put<SearchCategoryState>(SearchCategoryState(), permanent: true);
          }))
    ],
    initialRoute: '',

    ///home: CounterPage(),
  ));
}

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

1 回复

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


当然,关于Flutter状态管理插件ajwah_bloc的使用,下面是一个简单的代码案例来展示如何集成和使用它。ajwah_bloc是一个基于BLoC(Business Logic Component)模式的状态管理库,它允许你以响应式的方式管理应用的状态。

首先,确保你已经在pubspec.yaml文件中添加了ajwah_bloc依赖:

dependencies:
  flutter:
    sdk: flutter
  ajwah_bloc: ^latest_version  # 替换为最新版本号

然后,运行flutter pub get来安装依赖。

接下来,我们将创建一个简单的计数器应用来演示如何使用ajwah_bloc

1. 定义事件(Events)

首先,我们需要定义可能触发状态变化的事件。例如,增加计数器和减少计数器的事件。

// counter_events.dart
import 'package:ajwah_bloc/ajwah_bloc.dart';

class IncrementEvent extends Event {}

class DecrementEvent extends Event {}

2. 定义状态(States)

状态表示应用当前的视图或数据。在这个例子中,我们将有一个包含计数器的状态。

// counter_states.dart
import 'package:ajwah_bloc/ajwah_bloc.dart';

class CounterState extends State {
  final int count;

  CounterState({required this.count});

  @override
  List<Object?> get props => [count];
}

3. 定义BLoC

BLoC负责处理事件并发出新的状态。

// counter_bloc.dart
import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'counter_events.dart';
import 'counter_states.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState(count: 0));

  @override
  Stream<CounterState> mapEventToState(CounterEvent event) async* {
    if (event is IncrementEvent) {
      yield state.copyWith(count: state.count + 1);
    } else if (event is DecrementEvent) {
      yield state.copyWith(count: state.count - 1);
    }
  }
}

4. 创建UI层

在UI层,我们将使用BlocProvider来提供BLoC,并使用BlocBuilder来监听状态变化。

// main.dart
import 'package:flutter/material.dart';
import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'counter_bloc.dart';
import 'counter_events.dart';

void main() {
  runApp(BlocProvider<CounterBloc>(
    create: (_) => CounterBloc(),
    child: MyApp(),
  ));
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Counter App')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
              BlocBuilder<CounterBloc, CounterState>(
                builder: (context, state) {
                  return Text(
                    '${state.count}',
                    style: Theme.of(context).textTheme.headline4,
                  );
                },
              ),
            ],
          ),
        ),
        floatingActionButton: Column(
          mainAxisAlignment: MainAxisAlignment.end,
          children: <Widget>[
            FloatingActionButton(
              onPressed: () {
                context.read<CounterBloc>().add(IncrementEvent());
              },
              tooltip: 'Increment',
              child: Icon(Icons.add),
            ),
            SizedBox(height: 10),
            FloatingActionButton(
              onPressed: () {
                context.read<CounterBloc>().add(DecrementEvent());
              },
              tooltip: 'Decrement',
              child: Icon(Icons.remove),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的计数器应用,使用ajwah_bloc进行状态管理。BLoC处理增加和减少事件,并相应地更新状态。UI层监听这些状态变化并更新界面。

希望这个示例能够帮助你理解如何在Flutter项目中使用ajwah_bloc进行状态管理。如果有更多问题,欢迎继续提问!

回到顶部