jinyus / lite_ref

A lightweight dependency injection library for Dart and Flutter.
11 stars 3 forks source link

feat: bind(context) #30

Open passsy opened 4 months ago

passsy commented 4 months ago

Coming from package:provider I'm testing lite_ref and context_plus. All get the job done.

context_plus has some interesting (syntactic) features, I'd love to see in lite_ref, too.

bind

class ControlPageController extends BeaconController {
  static final ref = Ref<ControlPageController>();

  late final list = B.list<String>(['a', 'b', 'c']);
}
  @override
  Widget build(BuildContext context) {
    // bind ControlPageController to context, accessible via ControlPageController.ref for all children
    ControlPageController.ref.bind(context, () => ControlPageController());

    // directly available, without Builder()
    final controller = ControlPageController.ref.of(context);
    print(controller); // directly accessible

    return SizedBox();
  }

With lite_ref, passing anything down to the child widgets requires wrapping with LiteRefScope with overrides and a Builder to access it directly

  @override
  Widget build(BuildContext context) {
    return LiteRefScope(
      overrides: {
        // provides ControlPageController to all child widgets
        ControlPageController.ref..overrideWith((ctx) => ControlPageController()),
      },
      child: Builder(
        builder: (context) {
          // requires new context to access the controller
          final controller = ControlPageController.ref.of(context);
          print(controller);

          return SizedBox();
        }
      ),
    );
  }

The syntax it is not a dealbreaker. But it is quite verbose and adds two levels of nesting. That I have to define overrides feels off. It might be the only time I provide a value to child widgets, not overriding anything.

Would this bind API be possible for lite_ref, too?

yehorh commented 4 months ago

@passsy Hi, may I ask why you want to use overrides? Why didn't you just declare the constructor in a static field?

import 'package:flutter/material.dart';
import 'package:state_beacon/state_beacon.dart';

void main() {
  runApp(const LiteRefScope(child: MyApp()));
}

class MyController extends BeaconController {
  static final ref = Ref.scoped((context) => MyController());
  late final list = B.list<String>(['a', 'b', 'c']);
}

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

  @override
  Widget build(BuildContext context) {
    final controller = MyController.ref.of(context);
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: Scaffold(
        body: Center(
          child: Text(
            controller.list.watch(context).toString(),
          ),
        ),
      ),
    );
  }
}
jinyus commented 3 months ago

I actually prefer context_ref's approach to overriding but I found that I don't use the feature much so the nesting isn't much of an issue. I'm not opposed to adding it, but I can't make any guarantees right now.

passsy commented 3 months ago

Sorry, missed your response @yehorh!

I use it in three cases: Theming, Testing and Flavouring.

For theming I created my own DefaultTextStyle for my own ui package. I override the style regularly for a part of the subtree.

For testing I inject my dependencies like an api via the context. Obviously I can't call the fake constructor from my production code where I define my objects.

I also have multiple main.dart files for different clients (flavours). I know the type of object (Auth) I inject into the widget tree, but the exact implementation depends on the client (EmailPasswordAuth, FirebaseAuth).


Again, everything is already possible. It's just the syntax that feels too verbose when I switch from a context_ref to a lite_ref project.