jinyus / dart_beacon

A reactive primitive (signals) and state management library for dart and flutter
https://pub.dev/packages/state_beacon
27 stars 2 forks source link

feat: a ValueNotifier<T> toBeacon extension #104

Closed yehorh closed 6 months ago

yehorh commented 6 months ago

Description

Very useful will be extension to create Beacon from ValueNotifier.

simple example:

extension ValueNotifierUtils<T> on ValueNotifier<T> {
  ReadableBeacon<T> toBeacon({BeaconGroup? group, String? name}) {
    final beaconCreator = group ?? Beacon;
    final s = beaconCreator.writable<T>(value, name: name);
    void update() {
      s.value = value;
    }

    addListener(update);
    s.onDispose(() {
      removeListener(update);
    });
    return s;
  }
}

Sometimes it's necessary to create a TextField with two-way binding for both user input and updates from the business logic, but also have the ability to delegate validation and the TextField state to a Beacon. In such cases, it's easier to create a TextController and attach a Beacon to it for observing the field's state.

example with formz package:

  // Check validation or not
  final contactInfoConfirmed = Beacon.writable(false);

  // Controller for TextField
  final phone1Controller = TextEditingController();

  late final _phone1Beacon = phone1Controller.toBeacon();

  // Process state of the text field.
  late final phone1Field = Beacon.derived(
    () {
      final value = _phone1Beacon.value;
      final needValidation = contactInfoConfirmed.value;
      return switch ((value, needValidation)) {
        (TextEditingValue.empty, false) => PhoneInput.pure(value.text),
        (_, true) => PhoneInput.dirty(value.text),
        (_, false) => PhoneInput.pure(value.text),
      };
    },
  );

Is there a way to reduce the number of objects that need to be disposed?

jinyus commented 6 months ago

Thanks! I like this... will add it it the next version.

Maybe a TextEditingBeacon that wraps a TextEditingController would be useful.

Is there a way to reduce the number of objects that need to be disposed?

You could do this in a BeaconController or BeaconControllerMixin if using a StatefulWidget, then you wouldn't have to dispose the beacon manually.

yehorh commented 6 months ago

May be something to this:

 // Check validation or not
  final contactInfoConfirmed = Beacon.writable(false);

  // Controller for TextField
  final phone1Controller = TextEditingController();
  late final phone1Field = phone1Controller.toBeacon().derive((value) {
      final needValidation = contactInfoConfirmed.value;
      return switch ((value, needValidation)) {
        (TextEditingValue.empty, false) => PhoneInput.pure(value.text),
        (_, true) => PhoneInput.dirty(value.text),
        (_, false) => PhoneInput.pure(value.text),
      };
    },
  ),

I'm not sure about an interface but it would be more clearly.

jinyus commented 6 months ago

Lmk what you think about this, everything would be disposed automatically.

class SignupFormController with BeaconController {
  late final contactInfoConfirmed = B.writable(false);

  late final phone1Controller = TextEditingBeacon(group: B);

  late final phone1Field = B.derived(
    () {
      final needValidation = contactInfoConfirmed.value;
      final value = phone1Controller.value;

      return switch ((value, needValidation)) {
        (TextEditingValue.empty, false) => PhoneInput.pure(value.text),
        (_, true) => PhoneInput.dirty(value.text),
        (_, false) => PhoneInput.pure(value.text),
      };
    },
  );
}

final signUpRef = Ref.scoped((ctx) => SignupFormController());

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

  @override
  Widget build(BuildContext context) {
    final signUpController = signUpRef.of(context);

    //.. your form widget ..//
    return SizedBox.shrink();
  }
}

When SignUpPage is unmounted, all Beacons and TextEditingControllers gets cleaned up.

PS: Both ValueNotifier.toBeacon() and TextEditingBeacon() added in v0.45.0

yehorh commented 6 months ago

Thanks! I looks great!