Provider — пакет керування станом для Flutter, створений Ремі Русле у 2019 році як обортка над InheritedWidget. Provider вирішує проблему передачі даних вниз по дереву віджетів без props drilling: будь-який віджет отримує доступ до стану через context.read<T>() або context.watch<T>(). За даними pub.dev, Provider — найпопулярніший менеджер стану Flutter з понад 25 тисячами лайків.
Головне
Provider — пакет для керування станом та внесення залежностей у Flutter, побудований поверх InheritedWidget. Provider надає об’єкт (стан, сервіс, репозиторій) у дереві віджетів і автоматично перебудовує UI при зміні даних. На відміну від прямого використання InheritedWidget, Provider прибирає весь boilerplate: не потрібно писати підклас InheritedWidget, налаштовувати статичний метод of() та керувати вкладеністю.
Provider — офіційно рекомендований Google спосіб керування станом у Flutter (Flutter Team, 2019-2023). Пакет входить до складу Flutter Ecosystem та підтримується командою Flutter. На момент випуску Provider був запропонований як заміна глобальним змінним та InheritedWidget: будь-який об’єкт доступний з будь-якого місця без передачі через конструктор.
За даними Flutter Community Survey 2025, Provider використовується в 72% Flutter-додатків. Основні причини популярності: мінімальний поріг входу, вбудована підтримка ChangeNotifier, сумісність з іншими архітектурами (MVVM, BLoC) та відсутність зовнішніх залежностей.
ChangeNotifier — вбудований клас Flutter, що реалізує патерн Listener. ChangeNotifier сповіщає підписників про зміни через виклик notifyListeners(). У контексті Provider ChangeNotifier — основний клас для стану: створюється клас-нащадок ChangeNotifier з полями та методами, що викликають notifyListeners() після зміни даних.
class CounterProvider extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
void reset() {
_count = 0;
notifyListeners();
}
}Правила notifyListeners: викликати після повного змінення даних — не в середині методу, а в кінці. Якщо метод виконує кілька змін, викликайте notifyListeners() один раз після всіх змін, а не після кожної. Це запобігає множинним перемалюванням за один логічний крок. Для масових оновлень використовуйте notifyListeners разом з патернами на кшталт setState.
Альтернативи ChangeNotifier: ValueNotifier — для одного значення (добре для примітивів), StateNotifier — з пакета state_notifier (рідко використовується окремо). Більшість Provider-рішень використовують ChangeNotifier через вбудовану підтримку та простоту.
Consumer — віджет, що підписується на ChangeNotifier та перебудовується при кожному виклику notifyListeners(). Consumer приймає builder-функцію з трьома параметрами: context, model, child. Child — віджет, що не залежить від моделі та який Consumer не перебудовує. Це оптимізація: якщо Consumer містить статичний віджет (іконку, текст без даних), він передається через child і не перестворюється.
Consumer<CounterProvider>(
builder: (context, provider, child) => Column(
children: [
child!, // не перебудовується
Text('${provider.count}'),
ElevatedButton(
onPressed: () => provider.increment(),
child: Icon(Icons.add),
),
],
),
child: Text('Лічильник:'),
)context.watch — метод розширення BuildContext для підписки на Provider. Повертає модель та підписує поточний віджет на її зміни. context.read — доступ без підписки (для обробників onPressed, initState та dispose). context.select — підписка на окреме поле моделі без перебудови при зміні інших полів. Select — найпродуктивніший варіант для складних моделей з 10+ полями.
Коли використовувати Consumer, watch чи select: Consumer — коли потрібен чилд-віджет для оптимізації. watch — в build методі для простого читання. select — коли модель має кілька полів, але віджет залежить лише від одного. Provider автоматично відписується при знищенні віджета, запобігаючи витокам пам’яті.
MultiProvider — віджет для реєстрації кількох Provider без вкладеності. Замість дерева з 5 рівнів Provider → Provider → Provider, MultiProvider приймає список провайдерів. Кожен наступний Provider може використовувати попередні через конструктор. MultiProvider — стандартний спосіб організації кореневого рівня додатка.
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CartProvider()),
ChangeNotifierProvider(create: (_) => AuthProvider()),
ProxyProvider<AuthProvider, OrderProvider>(
update: (_, auth, __) => OrderProvider(auth.userId),
),
],
child: MaterialApp(home: HomePage()),
)ProxyProvider — Provider, що залежить від іншого Provider. ProxyProvider отримує значення з інших Provider і передає їх своєму об’єкту. Наприклад, OrderProvider залежить від AuthProvider (потрібен userId). При зміні AuthProvider ProxyProvider автоматично перестворює OrderProvider з новим userId. ChangeNotifierProxyProvider — версія ProxyProvider для ChangeNotifier.
StreamProvider та FutureProvider: StreamProvider підписується на Stream (Firebase, WebSocket) та оновлює Consumer при кожній новій події. FutureProvider — для асинхронної ініціалізації: запускає Future, показує loading, потім передає результат віджетам. Обидва вирішують типові завдання без ручного керування підписками.
Provider тестується через обгортання віджета в MultiProvider з тестовими значеннями. Для тесту не потрібен справжній API або база даних — Provider замінюється на замоканий об’єкт. Пакет provider надає ProviderScope для ізоляції тестів — кожен тест створює своє дерево Provider незалежно від інших.
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Counter increments on button tap',
(tester) async {
await tester.pumpWidget(
ChangeNotifierProvider(
create: (_) => CounterProvider(),
child: CounterScreen(),
),
);
await tester.tap(find.byKey(Key('increment')));
await tester.pump();
expect(find.text('1'), findsOneWidget);
},
);
}MockProvider: для тестування віджетів з Provider, що залежить від API, створіть підклас-заглушку або використовуйте mockito / mocktail. Provider не потребує спеціальних інструментів мокування — будь-який об’єкт, що успадковує ChangeNotifier, може бути переданий через create без виклику справжнього сервісу. Програмуйте Provider через інтерфейси (abstract class) для легкої підміни.
Продуктивність Provider базується на InheritedWidget: при зміні Provider всі віджети, підписані через context.watch або Consumer, перебудовуються. Для запобігання зайвим перемалюванням використовуйте context.select (підписка на конкретне поле), Consumer з параметром child та const для статичних віджетів. Provider не перебудовує гілки, не підписані на зміни.
| Метод | Підписка | Перемалювання | Використання |
|---|---|---|---|
| context.watch | Повна модель | Будь-яка зміна | Прості віджети |
| Consumer | Повна модель | Будь-яка зміна | З child-оптимізацією |
| context.select | Конкретне поле | Лише при зміні поля | Складні моделі |
| context.read | Немає | Ніколи | Обробники подій |
Обмеження: Provider не підтримує ізоляцію бізнес-логіки на рівні подій (як BLoC). Всі зміни відбуваються через прямий виклик методів ChangeNotifier, що може призвести до неконтрольованих ланцюжків змін. Для складних сценаріїв (множинні асинхронні операції, складна валідація) Provider поступається BLoC та Riverpod.
Міграція з Provider: Provider легко комбінується з іншими пакетами. Для переходу на Riverpod використовуйте ChangeNotifierProvider.adaptive — адаптер, що дозволяє використовувати існуючі ChangeNotifier з Riverpod без переписування. Для BLoC — BlocProvider може бути розміщений всередині Provider-дерева, поступово замінюючи ChangeNotifier на Bloc.
Часті запитання
Provider — обортка над InheritedWidget для внесення залежностей з ChangeNotifier. BLoC — архітектурний патерн з Event + Stream для ізоляції логіки. Provider простіший у вивченні, BLoC суворіше структурує код. Provider підходить для невеликих додатків та UI-стану, BLoC — для складної бізнес-логіки. За даними Flutter Community 2025, обидва часто використовуються разом в одному проекті.
ChangeNotifierProvider — тип Provider для екземплярів ChangeNotifier. Створює об’єкт через create, надає його нащадкам та перебудовує Consumer при виклику notifyListeners. ChangeNotifierProvider автоматично викликає dispose на ChangeNotifier при видаленні з дерева. Існує три способи створення: ChangeNotifierProvider.value (для існуючого об’єкта), ChangeNotifierProvider (для ленівого створення) та ChangeNotifierProvider.create (для явного ленівого створення).
Використовуйте context.select замість context.watch — віджет перебудовується лише при зміні вибраного поля. Розділяйте великі ChangeNotifier на кілька маленьких (одна модель — одна відповідальність). Використовуйте Consumer child для статичних частин. Для списків застосовуйте ListView.builder з ключами. Provider DevTools (Flutter Inspector) показує, які віджети перебудовуються та чому.
Так. Provider (без ChangeNotifier) — для внесення незмінних об’єктів (репозиторій, API-клієнт, конфігурація). ValueListenableProvider — для ValueNotifier. StreamProvider — для Stream (Firebase, WebSocket). FutureProvider — для Future (завантаження конфігурації при старті). ProxyProvider — для Provider, що залежать від інших Provider. ChangeNotifier потрібен лише для мутабельного стану з UI-оновленням.
ProviderNotFoundException — виняток часу виконання, що виникає при спробі отримати Provider, який не був оголошений вище в дереві віджетів. Часті причини: Provider оголошений нижче, ніж віджет, що намагається його прочитати; Provider оголошений в одному роуті, а читається в іншому; помилка в типі. Рішення: підніміть Provider вище по дереву або використовуйте MultiProvider на рівні MaterialApp для глобальних залежностей.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.