Flutter技术疑问:关于依赖注入、服务定位器、get_it、provider与bloc的困惑
Hey there! Let’s start by clearing up a couple of minor points in your initial assumptions to set the stage, then tackle your questions head-on.
Quick Corrections to Your Assumptions
- While
get_itis most commonly associated with the Service Locator pattern, it can also handle dependency injection (DI) via its registration methods (likeregisterSingletonorregisterFactory). The key difference fromprovideris thatget_ituses a global registry, whereasproviderinjects dependencies directly into the widget tree. providerisn’t just a DI tool—it’s a foundational state management framework. Bloc often usesproviderto make Bloc instances available to widgets in the tree, butprovidercan power its own state management workflows too (like withChangeNotifier).- Your point about Bloc separating UI, logic, and data is spot-on—Bloc’s event-driven model excels at keeping concerns cleanly separated.
Question 1: Why Combine get_it with Bloc/Provider? Is This a Source of Confusion?
Mixing these tools is actually a common (and often smart) practice in larger Flutter projects—here’s why, and how to avoid confusion:
Why Combine Them?
Think of it as splitting responsibilities:
get_itfor global, UI-agnostic services: Things like API clients, database managers, or shared utility classes that need to be accessible app-wide, regardless of the widget tree. Registering these once inget_itavoids having to pass them down through every widget constructor or wrap your entire app in a dozenProviderwidgets.provider/Bloc for UI-bound state: Bloc instances (or other state holders likeChangeNotifier) are often tied to a specific screen or feature. Usingproviderto inject these into the widget tree ensures they’re disposed when the screen is removed, preventing memory leaks. Plus, Bloc can pull its dependencies (like that API client) fromget_itinstead of having them passed in through the widget tree.
Avoiding Conceptual Confusion
Confusion only happens if you mix their roles. As long as you stick to this division:
- Use
get_itfor services that don’t depend on the widget tree and need to be global. - Use
providerfor state holders that are tied to a specific part of the UI and need to be scoped to that tree.
This separation actually makes code more maintainable—you’ll never find yourself registering a screen-specific Bloc in get_it or trying to access a global API client via a widget tree provider.
Question 2: Can provider Alone Implement MVVM (or Other Layered Architectures) as a Bloc Alternative?
Absolutely! provider is incredibly flexible and can power a clean MVVM setup that’s a great alternative to Bloc, especially for projects where Bloc’s event-driven model feels overkill.
How to Implement MVVM with provider
Here’s a simplified example to illustrate the pattern:
1. Model Layer (Data & Services)
This is your data source and business logic foundation—no UI dependencies here.
class User { final String name; User(this.name); } class ApiService { Future<List<User>> getUsers() async { // Simulate API call await Future.delayed(Duration(seconds: 1)); return [User('Alice'), User('Bob'), User('Charlie')]; } }
2. ViewModel Layer (State & Business Logic)
Use ChangeNotifier to manage state and handle user interactions. Dependencies (like ApiService) are injected via the constructor for testability.
import 'package:flutter/foundation.dart'; class UserViewModel extends ChangeNotifier { final ApiService _apiService; List<User> _users = []; bool _isLoading = false; List<User> get users => _users; bool get isLoading => _isLoading; UserViewModel(this._apiService); Future<void> fetchUsers() async { _isLoading = true; notifyListeners(); // Notify UI of loading state try { _users = await _apiService.getUsers(); } catch (e) { // Handle errors (e.g., show a snackbar via the UI layer) if (kDebugMode) print('Error fetching users: $e'); } finally { _isLoading = false; notifyListeners(); // Notify UI of updated state } } }
3. View Layer (UI)
The Flutter widget tree listens to the ViewModel’s state changes and triggers actions.
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class UserListPage extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider( create: (context) => UserViewModel(ApiService()), child: Scaffold( appBar: AppBar(title: const Text('User List')), body: Consumer<UserViewModel>( builder: (context, viewModel, child) { if (viewModel.isLoading) { return const Center(child: CircularProgressIndicator()); } return ListView.builder( itemCount: viewModel.users.length, itemBuilder: (context, index) { return ListTile(title: Text(viewModel.users[index].name)); }, ); }, ), floatingActionButton: FloatingActionButton( onPressed: () => context.read<UserViewModel>().fetchUsers(), child: const Icon(Icons.refresh), ), ), ); } }
Is This a Good Bloc Alternative?
Yes—here’s when it makes sense:
- Small to medium projects: If you don’t need complex event handling (like multiple event types per feature), this setup is lighter and has a lower learning curve than Bloc.
- Simple state flows: For features where state changes are tied directly to user actions (e.g., fetching data, toggling a switch),
ChangeNotifier+provideris more concise than defining Bloc events and states.
Key Tips for Success
- Keep ViewModels focused: Don’t put UI logic (like navigation) in the ViewModel—handle that in the widget layer.
- Inject dependencies: Use constructor injection (as shown) instead of pulling from
get_itdirectly in the ViewModel if possible—it makes unit testing easier. - Optimize rebuilds: Use
Selectorinstead ofConsumerif you only need to listen to specific parts of the ViewModel’s state, reducing unnecessary widget rebuilds.
内容的提问来源于stack exchange,提问作者Johnson




