Last active
August 14, 2025 17:31
-
-
Save evaisse/82b8bf8ad0280868e2bcd55d1065a735 to your computer and use it in GitHub Desktop.
using provider to lazy load service disregarding the widget tree order.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'package:provider/provider.dart'; | |
| class ServiceBuilder<T extends Service> { | |
| final T Function(BuildContext context) create; | |
| ServiceBuilder(this.create); | |
| Provider<T> asProvider(ValueNotifier<BuildContext> scope) => | |
| Provider<T>(create: (_) => create(scope.value)); | |
| @override | |
| bool operator ==(Object other) => other is ServiceBuilder<T>; | |
| @override | |
| int get hashCode => T.hashCode; | |
| } | |
| class ServicesFactory extends StatelessWidget { | |
| final Set<ServiceBuilder> services; | |
| final WidgetBuilder builder; | |
| const ServicesFactory({ | |
| super.key, | |
| required this.services, | |
| required this.builder, | |
| }); | |
| @override | |
| Widget build(BuildContext context) { | |
| final scope = ValueNotifier(context); | |
| final providers = services.map((s) => s.asProvider(scope)).toSet().toList(); | |
| return MultiProvider( | |
| providers: providers, | |
| builder: (context, child) { | |
| scope.value = context; | |
| return builder(context); | |
| }, | |
| ); | |
| } | |
| } | |
| class ServiceApp extends StatelessWidget { | |
| const ServiceApp({super.key}); | |
| @override | |
| Widget build(BuildContext context) { | |
| return MaterialApp( | |
| title: 'Service Management', | |
| theme: ThemeData(), | |
| home: ServicesFactory( | |
| services: { | |
| ServiceBuilder<LolaService>(MockedLolaService.fromContext), | |
| ServiceBuilder<LolaService>(LolaService.fromContext), | |
| ServiceBuilder<JohnService>(JohnService.fromContext), | |
| ServiceBuilder<LaraService>(LaraService.fromContext), | |
| }, | |
| builder: (context) => const ServiceDashboard(), | |
| ), | |
| ); | |
| } | |
| } | |
| /// A dashboard widget to display and interact with the services. | |
| class ServiceDashboard extends StatelessWidget { | |
| const ServiceDashboard({super.key}); | |
| @override | |
| Widget build(BuildContext context) { | |
| return Scaffold( | |
| appBar: AppBar(title: const Text('Service Management')), | |
| body: SingleChildScrollView( | |
| padding: const EdgeInsets.all(16.0), | |
| child: Center( | |
| child: Column( | |
| children: [ | |
| Text(context.read<LolaService>().status), | |
| Text(context.read<JohnService>().status), | |
| Text(context.read<LaraService>().status), | |
| ], | |
| ), | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| abstract class Service { | |
| String get status; | |
| Service.fromContext(BuildContext context); | |
| } | |
| class LolaService extends Service { | |
| @override | |
| String get status => 'Lola is observing quietly.'; | |
| LolaService.fromContext(super.context) : super.fromContext(); | |
| } | |
| class MockedLolaService extends LolaService { | |
| @override | |
| String get status => 'I\'m mocked Lola.'; | |
| MockedLolaService.fromContext(super.context) : super.fromContext(); | |
| } | |
| class JohnService extends Service { | |
| @override | |
| String get status => 'John is pondering, while lara ${lara.status}.'; | |
| final LaraService lara; | |
| JohnService.fromContext(super.context) | |
| : lara = context.read(), | |
| super.fromContext(); | |
| } | |
| class LaraService extends Service { | |
| @override | |
| String status = 'Lara is thoughtful.'; | |
| final JohnService Function() johnProvider; | |
| LaraService.fromContext(super.context) | |
| : johnProvider = (() => context.read<JohnService>()), | |
| super.fromContext(); | |
| } | |
| void main() { | |
| runApp(const ServiceApp()); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment