Skip to content

[Feature Request] Add useAcross() hook for cross-method data sharing #478

@yuluyao

Description

@yuluyao

[Feature Request] Add useAcross() hook for cross-method data sharing

Problem Statement

When decomposing UI into multiple methods within a HookWidget, there's often a need to share values across different methods. Current solutions have their own limitations:

Current Solutions and Their Problems:

1. Method Parameter Passing

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final controller = useTextEditingController();
    final focusNode = useFocusNode();
    
    return Column(
      children: [
        _buildInput(controller, focusNode),  // Too many parameters
        _buildButtons(controller, focusNode), // Repetitive passing
      ],
    );
  }
  
  Widget _buildInput(TextEditingController controller, FocusNode focusNode) { ... }
  Widget _buildButtons(TextEditingController controller, FocusNode focusNode) { ... }
}

Problems: Long parameter lists, repetitive passing, difficult to maintain

2. Inlining methods into build

Widget build(BuildContext context) {
  final controller = useTextEditingController();
  final focusNode = useFocusNode();
  
  return Column(
    children: [
      // All UI logic crammed into build method, hard to read
      TextField(controller: controller, focusNode: focusNode),
      Row(
        children: [
          ElevatedButton(onPressed: () => controller.clear(), ...),
          ElevatedButton(onPressed: () => focusNode.requestFocus(), ...),
        ],
      ),
    ],
  );
}

Problems: Bloated build method, poor readability, defeats the purpose of method decomposition

Proposed Solution: useAcross() Hook

useAcross allows sharing hook values across different methods within the same HookWidget without parameter passing:

typedef _Holder = ({
  TextEditingController controller,
  FocusNode focusNode,
  ValueNotifier<bool> isValid,
});

class MyWidget extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final controller = useTextEditingController();
    final focusNode = useFocusNode();
    
    // Store all hooks that need to be shared
    useAcross<_Holder>((
      controller: controller,
      focusNode: focusNode,
    ));
    
    return Column(
      children: [
        _buildInput(),      // No parameter passing needed
        _buildButton(),    // Clean and simple
      ],
    );
  }
  
  Widget _buildInput() {
    final _Holder(:controller, :focusNode) = useAcross<_Holder>();
    return TextField(controller: controller, focusNode: focusNode);
  }
  
  Widget _buildButton() {
    final _Holder(:controller) = useAcross<_Holder>();
    return Row(
      children: [
        ElevatedButton(onPressed: () => controller.clear(), ...),
      ],
    );
  }
}

Implementation

T useAcross<T>([T? value]) {
  final context = useContext();
  if (value != null) {
    _container[context] = value;
    return value;
  } else {
    return _container[context] as T;
  }
}

final Expando _container = Expando();

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions