stream_listener_widget 1.0.1 copy "stream_listener_widget: ^1.0.1" to clipboard
stream_listener_widget: ^1.0.1 copied to clipboard

A simple widget that listen streams without rebuilding its child (ex. to show a dialog)

stream_listener_widget #

A simple widget that listen streams without rebuilding its child (ex. to show a dialog)

Keep it simple #

  • A logic class (ViewModel, Controller, etc) should not make use of BuildContext
  • Triggering a popup or navigation belongs to View's responsibility

The StreamListener widget is as simple as Flutter's StreamBuilder widget.

  • It allows you to react to streams' events without rebuilding any widget
  • And it will clean up memory for you by cancelling all the given streams' subscriptions

Here is an example where our logic just trigger some events handled by the View in order to navigate or to to display an error dialog.

  class MyView extends StatelessWidget {
      final MyController logic;
      
      const MyView({super.key, required this.logic});
      
      void _onLoginSuccess(LoginSuccess e) {
        Navigator.of(context).pushNamed('/homePage');
      }
    
      void _onError(LoginError e) {
        showDialog(
          context: context,
          barrierDismissible: true,
          builder: (context) => Dialog(child: Text('An unknown error occured')),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return StreamListener(
          listeners: [
            (context) => logic.controller.stream.whereType<LoginSuccess>().listen(_onLoginSuccess),
            (context) => logic.controller.stream.whereType<LoginError>().listen(_onError),
          ],
          child: Scaffold(
            floatingActionButton: FloatingActionButton(
              onPressed: () => logic.submitForm('my_email', 'my_password'),
              child: const Text('Submit'),
            ),
          ),
        );
      }
  }
  
  /// Here's some Type classes that defines possible domain/logic's events
  sealed class MyEvent {}
  class LoginSuccess extends MyEvent {}
  class LoginError extends MyEvent {}
  • Our view handle View's logic
  • Our logic class handle Business logic
  • Responsibilities & dependencies are well separated
  • Our controller has been Domain Driven Designed and can be used elsewhere :)
  • By defining a Type to logic's events the Business logic is clear
  • The code is stable and testable

Here's what you should avoid to do (handling navigation, popup, etc in your logic class):

    class MyController {
        BuildContext context;
        
        Future<void> submitForm(String email, String password) {
            try {
                await authenticationApi.login(email, password);
                Navigator.of(context).pushNamed('/homePage');
            } catch(e) {
                showDialog(
                  context: context,
                  barrierDismissible: true,
                  builder: (context) => Dialog(child: Text('An unknown error occured')),
                );
            }
        }
    }
    
    
    class MyView extends StatefulWidget {
      final logic = MyController();
      
      const MyView({super.key});
    
      @override
      Widget build(BuildContext context) {
        logic.context = context; // That's really ugly
        return Scaffold(
            floatingActionButton: FloatingActionButton(
              onPressed: () => logic.submitForm('my_email', 'my_password'),
              child: const Text('Submit'),
            ),
          );
      }
    }

But wait ?! The example above is shorter !

  • Yes ! But we haven't separated View logic & Business logic
  • Our controller is doing everything while the View is doing nothing
  • Responsibilities and dependencies are mixed up
  • Your controller has been designed for this specific View
  • BuildContext is used after a Future/async gap which is forbidden because it can be unstable
4
likes
150
points
37
downloads

Publisher

unverified uploader

Weekly Downloads

A simple widget that listen streams without rebuilding its child (ex. to show a dialog)

Repository (GitLab)
View/report issues

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on stream_listener_widget