Skip to content

Instantly share code, notes, and snippets.

@thorizer
Forked from boformer/0_main.dart
Created March 16, 2022 20:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thorizer/b448c8ecf5ace69346478e7f26008ea7 to your computer and use it in GitHub Desktop.
Save thorizer/b448c8ecf5ace69346478e7f26008ea7 to your computer and use it in GitHub Desktop.
Flutter Service Architecture
import 'package:architecture_playground/home_page.dart';
import 'package:architecture_playground/services.dart';
import 'package:flutter/material.dart';
void main() async {
// perform long-running tasks before "runApp" to display the native splash screen
final services = await Services.initialize();
runApp(ServicesProvider(
services: services,
child: App(),
));
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Architecture Demo',
home: HomePage(),
);
}
}
import 'package:flutter/widgets.dart';
import 'package:rxdart/rxdart.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// Service container
class Services {
final SharedPreferences sharedPrefs;
final DataService dataService;
final AuthService authService;
Services(this.sharedPrefs, this.dataService, this.authService);
static Future<Services> initialize() async {
final sharedPrefs = await SharedPreferences.getInstance();
final dataService = DataService();
await dataService.initialize();
// service that depends on other services
final authService = AuthService(sharedPrefs, dataService);
return Services(sharedPrefs, dataService, authService);
}
static Services of(BuildContext context) {
// A bit different from a normal inherited widget. Widgets can call this from initState,
// and it is assumed that the services never change during the lifetime of the app
final provider = context.ancestorInheritedElementForWidgetOfExactType(ServicesProvider).widget as ServicesProvider;
return provider.services;
}
}
class ServicesProvider extends InheritedWidget {
final Services services;
ServicesProvider({Key key, this.services, Widget child}) : super(key: key, child: child);
@override
bool updateShouldNotify(ServicesProvider old) {
if (services != old.services) {
throw Exception('Services must be constant!');
}
return false;
}
}
// Example services:
class AuthService {
final SharedPreferences _sharedPrefs;
final DataService _dataService;
AuthService(this._sharedPrefs, this._dataService);
// getters, fields, methods, obervables...
}
class DataService {
Observable<int> get magicNumber$ => _magicNumber$;
final _magicNumber$ = BehaviorSubject<int>();
DataService() {}
Future<void> initialize() async {
// open database, contact server...
await Future.delayed(Duration(seconds: 2));
_magicNumber$.add(42);
}
void increaseMagic() {
_magicNumber$.add(_magicNumber$.value + 1);
}
}
import 'package:architecture_playground/home_bloc.dart';
import 'package:architecture_playground/services.dart';
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
HomeBloc _bloc;
@override
void initState() {
super.initState();
// "HomeBloc" is a scoped service. There's no DI, so we have to initialize it manually.
// if required, you can create another provider InheritedWidget to make it available to child widgets
final services = Services.of(context);
_bloc = HomeBloc(services.dataService);
}
@override
void dispose() {
_bloc.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Architecture Demo')),
body: StreamBuilder<String>(
stream: _bloc.magicString$,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(
child: Text(snapshot.data),
);
} else {
return Loading();
}
},
),
);
}
}
class Loading extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: CircularProgressIndicator(),
);
}
}
import 'package:architecture_playground/services.dart';
import 'package:rxdart/rxdart.dart';
class HomeBloc {
final DataService _dataService;
Observable<String> get magicString$ => _magicString$;
Observable<String> _magicString$;
HomeBloc(this._dataService) {
_magicString$ = _dataService.magicNumber$.map((n) => 'The magic number is $n');
}
void dispose() {
// do stuff when UI model is disposed
}
}
name: architecture_playground
description: A new Flutter application.
version: 1.0.0+1
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
shared_preferences: ^0.4.3
rxdart: ^0.19.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment