Build a Todo App with Flutter(18)
A repository is part of the business layer.
-
• A repositorydepends on one or more data providers that have no business value, and combines their public API into APIs that provide business value. -
• A repositorylayer helps abstract data acquisition from the rest of the application, allowing us to change where/how data is being stored without affecting other parts of the app.
Instantiating the repository requires specifying a TodosApi, we can add it as a dependency in the pubspec.yaml.
name:todos_repositorydescription: A repository that handles todo related requests.version: 0.1.0+1publish_to: noneenvironment: sdk: ^3.11.0dependencies: todos_api:path: ../todos_apidev_dependencies: mocktail: ^1.0.5 test: ^1.31.0 very_good_analysis:^10.2.0
classTodosRepository{ const TodosRepository({required TodosApi todosApi}) : _todosApi = todosApi; final TodosApi _todosApi; Stream<List<Todo>> getTodos() => _todosApi.getTodos(); Future<void> saveTodo(Todo todo) => _todosApi.saveTodo(todo); Future<void> deleteTodo(String id) => _todosApi.deleteTodo(id); Future<int> clearCompleted() => _todosApi.clearCompleted(); Future<int> completeAll({requiredbool isCompleted}) => _todosApi.completeAll(isCompleted: isCompleted); void dispose() => _todosApi.close();}
Entrypoint
The flutter app’s entrypoint is main.dart, we can define three versions as below:
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized();final todosApi = LocalStorageTodosApi( plugin: await SharedPreferences.getInstance(), ); bootstrap(todosApi: todosApi);}
The concrete implementation of the local_storage_todos_api is instantiated within each entrypoint.
Bootstraping
bootstrap.dart loads the BlocObserver and creates the instance of TodosRepository.
void bootstrap({required TodosApi todosApi}) { FlutterError.onError = (details) { log(details.exceptionAsString(), stackTrace: details.stack); }; PlatformDispatcher.instance.onError = (error, stack) { log(error.toString(), stackTrace: stack);returntrue; }; Bloc.observer = const AppBlocObserver(); runApp(App(createTodosRepository: () => TodosRepository(todosApi: todosApi)));}
App
App wraps a RepositoryProvider widget that provides the repository to all children.
Both the EditTodoPage and HomePage subtrees are descendents, all the blocs and cubits can access the repository.
AppView creates the MaterialApp and configures the theme and localizations.
classAppextendsStatelessWidget{ const App({required this.createTodosRepository, super.key}); final TodosRepository Function() createTodosRepository; @override Widget build(BuildContext context) {return RepositoryProvider<TodosRepository>( create: (_) => createTodosRepository(), dispose: (repository) => repository.dispose(), child: const AppView(), ); }}classAppViewextendsStatelessWidget{ const AppView({super.key}); @override Widget build(BuildContext context) {return MaterialApp( theme: FlutterTodosTheme.light, darkTheme: FlutterTodosTheme.dark, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: const HomePage(), ); }}
Theme
lib/theme/theme.dart provides theme definition for light and dark mode.
classFlutterTodosTheme{ static ThemeData get light {return ThemeData( appBarTheme: const AppBarTheme( backgroundColor: Color.fromARGB(255, 117, 208, 247), ), colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF13B9FF)), snackBarTheme: const SnackBarThemeData( behavior: SnackBarBehavior.floating, ), ); } static ThemeData get dark {return ThemeData( appBarTheme: const AppBarTheme( backgroundColor: Color.fromARGB(255, 16, 46, 59), ), colorScheme: ColorScheme.fromSeed( brightness: Brightness.dark, seedColor: const Color(0xFF13B9FF), ), snackBarTheme: const SnackBarThemeData( behavior: SnackBarBehavior.floating, ), ); }}
夜雨聆风