搜尋套件、儲存庫與 APP 分類

套件詳情v1.7.0

hooked_bloc

一個簡化 Bloc/Cubit 注入和使用的 Flutter 套件。

AndroidiOSWebmacOSWindowsLinux
近 30 天下載
9020
喜歡數
94
Pub 分數
160

安裝套件

flutter pub add hooked_bloc

套件介紹

根據套件公開資料產生的選型評估,僅供參考。

Hooked_Bloc 是一個 Flutter 套件,透過利用鈎子簡化了 Bloc 和 Cubit 類別的注入與使用。它讓開發者能以簡潔、反應式語法直接在組件中存取狀態管理邏輯,減少重複程式碼並提升程式碼可讀性。此套件專為與 Flutter 的現代元件模型無縫整合而設計,支援所有主要平台,包括 Android、iOS、Web、macOS、Windows 和 Linux。該套件透過結合 Bloc/Cubit 的強大功能與鈎子的簡單性,提升開發人員的生產力。

適用情境

  • 簡化複雜UI的狀態管理
  • 減少 Bloc/Cubit 使用中的樣板程式碼
  • 使用鈎子實現反應式小部件更新
  • 簡化狀態類的依賴注入
  • 建構可擴展的 Flutter 應用程式

主要特色

  • 內建的基於鉤子的 Bloc/Cubit 存取
  • 狀態管理的最小範本程式碼
  • 與 Flutter 的 widget 樹無縫整合
  • 支援所有 Flutter 平台
  • 簡潔、易讀的語法用於狀態處理

適合對象

  • 使用 Bloc/Cubit 模式的開發人員
  • 尋求更乾淨狀態管理的團隊
  • 需要反應式 UI 更新的專案
  • Flutter 應用針對多個平台
  • 開發者採用函數式元件設計

使用建議

  • 需要熟悉 Flutter hooks
  • 可能不適合非常簡單的狀態需求
  • 最佳搭配其他狀態管理實踐使用
  • 與非鉤子基礎的組件架構不相容
  • 依賴於 Bloc/Cubit 實例的正確設定

原始 README

以下為關聯原始碼儲存庫的 README 原文快照,最新內容請造訪原始碼儲存庫。



Build   codecov   pub package   stars   GitHub license  


Hooked Bloc

Flutter package that simplifies injection and usage of Bloc/Cubit. The library is based on the concept of hooks originally introduced in React Native and adapted to Flutter. Flutter hooks allow you to extract view's logic into common use cases and reuse them, which makes writing widgets faster and easier.

Contents

Motivation

When you want to use Bloc/Cubit in your application you have to provide an instance of the object down the widgets tree for state receivers. This is mostly achieved by BlocBuilder along with BlocProvider and enlarges complexity of the given widget.

Each time you have to use BlocBuilder, BlocListener or BlocSelector. What if we could use the power of Flutter hooks?

So, instead of this:

  @override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: ...,
    body: BlocProvider<RealLifeCubit>(
      create: (context) =>
      RealLifeCubit()
        ..loadData(),
      child: BlocListener<RealLifeCubit, hooked.BuildState>(
        listenWhen: (_, state) => state is ErrorState,
        listener: (context, state) {
          // Show some view on event
        },
        child: BlocBuilder<RealLifeCubit, hooked.BuildState>(
          buildWhen: (_, state) =>
              [LoadedState, LoadingState, ShowItemState]
                  .contains(state.runtimeType),
          builder: (BuildContext context, hooked.BuildState state) {
            return // Build your widget using `state`
          },
        ),
      ),
    ),
  );
}

We can have this:

  @override
Widget build(BuildContext context) {
  final cubit = useBloc<RealLifeCubit>();

  useBlocListener<RealLifeCubit, BuildState>(cubit, (cubit, value, context) {
    // Show some view on event
  }, listenWhen: (state) => state is ErrorState);

  final state = useBlocBuilder(
    cubit,
    buildWhen: (state) =>
        [LoadedState, LoadingState, ShowItemState].contains(
          state.runtimeType,
        ),
  );

  return // Build your widget using `state`
}

This code is functionally equivalent to the previous example. It still rebuilds the widget in the proper way and the right time. Whole logic of finding adequate Cubit/Bloc and providing current state is hidden in useBloc and useBlocBuilder hooks.

Full example can be found in here

Setup

Install package

Run command:

flutter pub add hooked_bloc

Or manually add the dependency in the pubspec.yaml

dependencies:
  # Library already contains flutter_hooks package
  hooked_bloc:

After that you can (it's optional) initialize the HookedBloc:

void main() async {
  // With GetIt or Injectable
  await configureDependencies();

  runApp(
    HookedBlocConfigProvider(
      injector: () => getIt.get,
      builderCondition: (state) => state != null, // Global build condition
      listenerCondition: (state) => state != null, // Global listen condition
      child: const MyApp(),
    )
  );

  // Or you can omit HookedBlocInjector(...)
  // and allow library to find the cubit in the widget tree
}

Then you can simply start writing your widget with hooks

// Remember to inherit from HookWidget
class MyApp extends HookWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // At start obtain a cubit instance
    final cubit = useBloc<CounterCubit>();
    // Then observe state's updates
    // `buildWhen` param will override builderCondition locally
    final state = useBlocBuilder(cubit, buildWhen: (state) => state <= 10);
    // Create a listener for the side-effect
    useBlocListener(cubit, (cubit, value, context) {
      ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(content: Text("Button clicked"),
          ));
    });

    // Build widget's tree without BlocProvider
    return MaterialApp(
      home: Scaffold(
        floatingActionButton: FloatingActionButton(
          onPressed: () => cubit.increment(), // Access cubit in tree
          child: const Icon(Icons.add),
        ),
        // Consume state without BlocBuilder
        body: Center(child: Text("The button has been pressed $state times")),
      ),
    );
  }
}

Basics

Existing hooks

Hooked Bloc already comes with a few reusable hooks:

Name Description
useBloc Returns required Cubit/Bloc
useBlocFactory Returns expected Cubit/Bloc by creating it via provided factory
useBlocBuilder Returns current Cubit/Bloc state - similar to BlocBuilder
useBlocComparativeBuilder Returns current Cubit/Bloc state basing on comparison result
useBlocListener Invokes callback - similar to BlocListener
useBlocComparativeListener Invokes callback basing on state comparison result
useActionListener Invokes callback, but independent of Bloc/Cubit state
useBloc

useBloc hook tries to find Cubit using the cubit provider, or - if not specified - looks into the widget tree.

  @override
Widget build(BuildContext context) {
  // The hook will provide the expected object
  final cubit = useBloc<SimpleCubit>(
    // For default hook automatically closes cubit
    closeOnDispose: true,
  );

  return // Access provided cubit
}
useBlocFactory

useBlocFactory hook tries to find factory using provided injection method and then returns cubit created by it

class SimpleCubitFactory extends BlocFactory<SimpleCubit> {
  bool _value = true;

  @override
  SimpleCubit create() {
    return _value ? SimpleCubitA() : SimpleCubitB();
  }

  // This is example method which you can add to configure your cubit creation on run
  void configure(bool value) {
    _value = value;
  }
}

@override
Widget build(BuildContext context) {
  // The hook will provide the expected object
  final cubit = useBlocFactory<SimpleCubit, SimpleCubitFactory>(
    onCubitCreate: (cubitFactory) {
      cubitFactory.configure(false);
    }
  );

  return // Access provided cubit
}
useBlocBuilder

useBlocBuilder hook rebuilds the widget when new state appears


final CounterCubit cubit = CounterCubit("My cubit");

@override
Widget build(BuildContext context) {
  // The state will be updated along with the widget
  // For default the state will be updated basing on `builderCondition`
  final int state = useBlocBuilder(cubit);

  return // Access provided state
}

useBlocComparativeBuilder

useBlocComparativeBuilder hook rebuilds the widget for a new state and positive comparison result


final CounterCubit cubit = CounterCubit("My cubit");

@override
Widget build(BuildContext context) {
  // The state will be updated along with the widget
  // We can compare state's changes to allow rebuild
  final state = useBlocComparativeBuilder(
    cubit,
    buildWhen: (int previous, int current) {
      return current != previous;
    },
  );

  return // Access provided state
}

useBlocListener

useBlocListener hook allows to observe cubit's states that represent action (e.g. show Snackbar)


final EventCubit cubit = EventCubit();

@override
Widget build(BuildContext context) {
  // Handle state as event independently of the view state
  useBlocListener(cubit, (_, value, context) {
    _showMessage(context, (value as ShowMessage).message);
  }, listenWhen: (state) => state is ShowMessage);

  return // Build your widget
}
useBlocComparativeListener

useBlocComparativeListener hook allows to observe and compare cubit's states that represent action (e.g. show Snackbar)


final EventCubit cubit = EventCubit();

@override
Widget build(BuildContext context) {
  // Handle state as event independently of the view state
  // We can compare state changes to allow listener function to be called
  useBlocComparativeListener(
    cubit,
    (_, value, context) {
       _showMessage(context, (value as ShowMessage).message);
    },
    listenWhen: (previousState, currentState) => previousState is! ShowMessage && currentState is ShowMessage,
    );

  return // Build your widget
}
useActionListener

useActionListener hook is similar to the useBlocListener but listens to the stream different than state's stream and can be used for actions that require a different flow of notifying.

Because of that your bloc/cubit must use BlocActionMixin

class MessageActionCubit extends EventCubit with BlocActionMixin<String, BuildState> {

  // The method used to publish events
  @override
  void dispatch(String action) {
    super.dispatch(action);
  }
}

Then, consume results as you would do with useBlocListener

  @override
Widget build(BuildContext context) {
  // Handle separate action stream with values other than a state type
  useActionListener(
    cubit, 
    (String action) {
      _showMessage(context, action);
    },
    // If you need, you can filter actions
    actionWhen: (previousAction, action) => true,
  );

  return // Build your widget
}

Instead of BlocActionMixin you can use one of our classes: ActionCubit or ActionBloc

class MessageActionCubit extends ActionCubit<BuildState, String>  {
  // The method used to publish events
  @override
  void dispatch(String action) {
    super.dispatch(action);
  }
}
class MessageActionBloc extends ActionBloc<BuildState, BlocEvent, String>  {
  // The method used to publish events
  @override
  void dispatch(String action) {
    super.dispatch(action);
  }
}

Contribution

We accept any contribution to the project!

Suggestions of a new feature or fix should be created via pull-request or issue.

feature request:
  • Check if feature is already addressed or declined

  • Describe why this is needed

    Just create an issue with label enhancement and descriptive title. Then, provide a description and/or example code. This will help the community to understand the need for it.

  • Write tests for your hook

    The test is the best way to explain how the proposed hook should work. We demand a complete test before any code is merged in order to ensure cohesion with existing codebase.

  • Add it to the README and write documentation for it

    Add a new hook to the existing hooks table and append sample code with usage.

Fix
  • Check if bug was already found

  • Describe what is broken

    The minimum requirement to report a bug fix is a reproduction path. Write steps that should be followed to find a problem in code. Perfect situation is when you give full description why some code doesn't work and a solution code.

  • Write tests for your hook

    The test should show that your fix corrects the problem. You can start with straightforward test and then think about potential edge cases or other places that can be broken.

  • Add it to the README and write documentation for it

    If your fix changed behavior of the library or requires any other extra steps from user, this should be fully described in README.

Contributors

所屬分類

依賴

依賴清單 5 項

原始碼儲存庫

使用此套件的專案

暫無相關資料。