Provider: co to jest, menedżer stanu aplikacji Flutter

Autor: IT Sectr Opublikowano: 2026-02-19 Czas czytania: 7 min

Provider — pakiet do zarządzania stanem dla Flutter, stworzony przez Rémi Rousselet w 2019 roku jako nakładka na InheritedWidget. Provider rozwiązuje problem przesyłania danych w dół drzewa widgetów bez props drilling: każdy widget uzyskuje dostęp do stanu przez context.read<T>() lub context.watch<T>(). Według danych pub.dev, Provider jest najpopularniejszym menedżerem stanu Flutter z ponad 25 tysiącami polubień.

Najważniejsze

  • ChangeNotifier — klasa z notifyListeners do powiadamiania subskrybentów o zmianie
  • Consumer — widget przebudowujący się przy zmianie ChangeNotifier
  • MultiProvider — kompozycja wielu Provider bez zagnieżdżania
  • ProxyProvider — Provider zależny od innego Provider
  • context.read / context.watch — metody odczytu stanu bez i z subskrypcją

Co to jest Provider?

Provider — pakiet do zarządzania stanem i wstrzykiwania zależności we Flutter, zbudowany na bazie InheritedWidget. Provider udostępnia obiekt (stan, serwis, repozytorium) w drzewie widgetów i automatycznie przebudowuje UI przy zmianie danych. W przeciwieństwie do bezpośredniego użycia InheritedWidget, Provider eliminuje cały boilerplate: nie trzeba pisać podklasy InheritedWidget, konfigurować statycznej metody of() ani zarządzać zagnieżdżeniem.

Provider — oficjalnie zalecany przez Google sposób zarządzania stanem we Flutter (Flutter Team, 2019-2023). Pakiet wchodzi w skład Flutter Ecosystem i jest wspierany przez zespół Flutter. W momencie wydania Provider został zaproponowany jako zamiennik globalnych zmiennych i InheritedWidget: każdy obiekt jest dostępny z dowolnego miejsca bez przekazywania przez konstruktor.

Według danych Flutter Community Survey 2025, Provider jest używany w 72% aplikacji Flutter. Główne przyczyny popularności: minimalny próg wejścia, wbudowane wsparcie ChangeNotifier, zgodność z innymi architekturami (MVVM, BLoC) i brak zewnętrznych zależności.

ChangeNotifier i notifyListeners

ChangeNotifier — wbudowana klasa Flutter, implementująca wzorzec Listener. ChangeNotifier powiadamia subskrybentów o zmianie przez wywołanie notifyListeners(). W kontekście Provider ChangeNotifier jest główną klasą dla stanu: tworzy się klasę dziedziczącą po ChangeNotifier z polami i metodami wywołującymi notifyListeners() po zmianie danych.

Dart
class CounterProvider extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }

  void reset() {
    _count = 0;
    notifyListeners();
  }
}

Zasady notifyListeners: wywoływać po pełnej zmianie danych — nie w środku metody, ale na końcu. Jeśli metoda wykonuje kilka zmian, wywołaj notifyListeners() raz po wszystkich zmianach, a nie po każdej. Zapobiega to wielokrotnemu przerysowywaniu w jednym logicznym kroku. W przypadku masowych aktualizacji używaj notifyListeners w połączeniu z wzorcami podobnymi do setState.

Alternatywy dla ChangeNotifier: ValueNotifier — dla pojedynczej wartości (dobry dla prymitywów), StateNotifier — z pakietu state_notifier (rzadko używany samodzielnie). Większość rozwiązań Provider używa ChangeNotifier ze względu na wbudowane wsparcie i prostotę.

Consumer i context.watch

Consumer — widget subskrybujący ChangeNotifier i przebudowujący się przy każdym wywołaniu notifyListeners(). Consumer przyjmuje funkcję builder z trzema parametrami: context, model, child. Child — widget niezależny od modelu, którego Consumer nie przebudowuje. To optymalizacja: jeśli wewnątrz Consumer znajduje się statyczny widget (ikona, tekst bez danych), jest on przekazywany przez child i nie jest odtwarzany.

Dart
Consumer<CounterProvider>(
  builder: (context, provider, child) => Column(
    children: [
      child!, // nie przebudowuje się
      Text('${provider.count}'),
      ElevatedButton(
        onPressed: () => provider.increment(),
        child: Icon(Icons.add),
      ),
    ],
  ),
  child: Text('Licznik:'),
)

context.watch — metoda rozszerzenia BuildContext do subskrypcji Provider. Zwraca model i subskrybuje bieżący widget na jego zmiany. context.read — dostęp bez subskrypcji (dla handlerów onPressed, initState i dispose). context.select — subskrypcja konkretnego pola modelu bez przebudowy przy zmianie innych pól. Select — najbardziej wydajna opcja dla złożonych modeli z 10+ polami.

Kiedy używać Consumer, watch lub select: Consumer — gdy potrzebny jest child-widget do optymalizacji. watch — w metodzie build do prostego odczytu. select — gdy model ma wiele pól, ale widget zależy tylko od jednego. Provider automatycznie anuluje subskrypcję przy zniszczeniu widgetu, zapobiegając wyciekom pamięci.

MultiProvider i ProxyProvider

MultiProvider — widget do rejestracji wielu Provider bez zagnieżdżania. Zamiast drzewa z 5 poziomami Provider → Provider → Provider, MultiProvider przyjmuje listę providerów. Każdy kolejny Provider może używać poprzednich przez konstruktor. MultiProvider — standardowy sposób organizacji głównego poziomu aplikacji.

Dart
MultiProvider(
  providers: [
    ChangeNotifierProvider(create: (_) => CartProvider()),
    ChangeNotifierProvider(create: (_) => AuthProvider()),
    ProxyProvider<AuthProvider, OrderProvider>(
      update: (_, auth, __) => OrderProvider(auth.userId),
    ),
  ],
  child: MaterialApp(home: HomePage()),
)

ProxyProvider — Provider zależny od innego Provider. ProxyProvider pobiera wartości z innych providerów i przekazuje je do swojego obiektu. Na przykład OrderProvider zależy od AuthProvider (potrzebuje userId). Przy zmianie AuthProvider ProxyProvider automatycznie odtwarza OrderProvider z nowym userId. ChangeNotifierProxyProvider — wersja ProxyProvider dla ChangeNotifier.

StreamProvider i FutureProvider: StreamProvider subskrybuje strumień (Firebase, WebSocket) i aktualizuje Consumer przy każdym nowym zdarzeniu. FutureProvider — do asynchronicznej inicjalizacji: uruchamia Future, pokazuje ładowanie, następnie przekazuje wynik widgetom. Oba rozwiązują typowe zadania bez ręcznego zarządzania subskrypcjami.

Testowanie Provider

Provider testuje się przez owinięcie widgetu w MultiProvider z wartościami testowymi. Do testu nie jest potrzebne prawdziwe API ani baza danych — Provider jest zastępowany zamockowanym obiektem. Pakiet provider udostępnia ProviderScope do izolacji testów — każdy test tworzy własne drzewo Provider niezależne od innych.

Dart
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: do testowania widgetów z Providerem zależnym od API, utwórz podklasę-zastępkę lub użyj mockito / mocktail. Provider nie wymaga specjalnych narzędzi do mockowania — każdy obiekt dziedziczący po ChangeNotifier może być przekazany przez create bez wywołania prawdziwego serwisu. Programuj Provider przez interfejsy (abstract class) dla łatwej zamiany.

Provider: wydajność i ograniczenia

Wydajność Provider opiera się na InheritedWidget: przy zmianie Provider wszystkie widgety subskrybujące przez context.watch lub Consumer są przebudowywane. Aby zapobiec niepotrzebnemu przerysowywaniu, używaj context.select (subskrypcja konkretnego pola), Consumer z parametrem child oraz const dla statycznych widgetów. Provider nie przebudowuje gałęzi drzewa, które nie są subskrybowane na zmiany.

MetodaSubskrypcjaPrzerysowanieZastosowanie
context.watchPełny modelKażda zmianaProste widgety
ConsumerPełny modelKażda zmianaZ optymalizacją child
context.selectKonkretne poleTylko przy zmianie polaZłożone modele
context.readBrakNigdyHandlery zdarzeń

Ograniczenia: Provider nie obsługuje izolacji logiki biznesowej na poziomie Event (jak BLoC). Wszystkie zmiany odbywają się przez bezpośrednie wywołanie metod ChangeNotifier, co może prowadzić do niekontrolowanych łańcuchów zmian. W przypadku złożonych scenariuszy (wielokrotne operacje asynchroniczne, złożona walidacja) Provider ustępuje BLoC i Riverpod.

Migracja z Provider: Provider łatwo łączy się z innymi pakietami. Aby przejść na Riverpod, użyj ChangeNotifierProvider.adaptive — adaptera umożliwiającego użycie istniejących ChangeNotifier z Riverpod bez przepisywania. Dla BLoC — BlocProvider może być umieszczony wewnątrz drzewa Provider, stopniowo zastępując ChangeNotifier na Bloc.

Często zadawane pytania

Czym różni się Provider od BLoC?

Provider — nakładka na InheritedWidget do wstrzykiwania zależności z ChangeNotifier. BLoC — wzorzec architektoniczny z Event + Stream do izolacji logiki. Provider jest łatwiejszy do nauczenia, BLoC ściślej strukturyzuje kod. Provider nadaje się do małych aplikacji i stanu UI, BLoC — do złożonej logiki biznesowej. Według danych Flutter Community 2025, oba są często używane razem w jednym projekcie.

Czym jest ChangeNotifierProvider?

ChangeNotifierProvider — typ Provider dla instancji ChangeNotifier. Tworzy obiekt przez create, udostępnia go potomkom i przebudowuje Consumer przy wywołaniu notifyListeners. ChangeNotifierProvider automatycznie wywołuje dispose na ChangeNotifier po usunięciu z drzewa. Istnieją trzy sposoby tworzenia: ChangeNotifierProvider.value (dla istniejącego obiektu), ChangeNotifierProvider (dla lazy-tworzenia) i ChangeNotifierProvider.create (dla jawnego lazy).

Jak uniknąć niepotrzebnego przerysowywania z Provider?

Używaj context.select zamiast context.watch — widget przerysowuje się tylko przy zmianie wybranego pola. Dziel duże ChangeNotifier na kilka małych (jeden model — jedna odpowiedzialność). Używaj Consumer child dla statycznych części. Dla list stosuj ListView.builder z kluczami. Provider DevTools (Flutter Inspector) pokazuje, które widgety się przerysowują i dlaczego.

Czy można używać Provider bez ChangeNotifier?

Tak. Provider (bez ChangeNotifier) — do wstrzykiwania niezmiennych obiektów (repozytorium, klient API, konfiguracja). ValueListenableProvider — dla ValueNotifier. StreamProvider — dla strumienia (Firebase, WebSocket). FutureProvider — dla Future (ładowanie konfiguracji przy starcie). ProxyProvider — dla Providerów zależnych od innych Providerów. ChangeNotifier jest potrzebny tylko dla stanu mutowalnego z aktualizacją UI.

Czym jest ProviderNotFoundException?

ProviderNotFoundException — wyjątek czasu wykonania, występujący przy próbie uzyskania Provider, który nie został zadeklarowany wyżej w drzewie widgetów. Częste przyczyny: Provider zadeklarowany niżej niż widget próbujący go odczytać; Provider zadeklarowany w jednym routerze, a odczytywany w innym; literówka w typie. Rozwiązanie: przesuń Provider wyżej w drzewie lub użyj MultiProvider na poziomie MaterialApp dla globalnych zależności.

Podsumowanie

  • Provider — nakładka na InheritedWidget do zarządzania stanem i DI we Flutter
  • ChangeNotifier — klasa z notifyListeners do reaktywnej aktualizacji UI
  • Consumer i context.watch — subskrypcja zmian z automatycznym przerysowaniem
  • MultiProvider — kompozycja providerów bez zagnieżdżania drzewa
  • ProxyProvider — łańcuch zależności między Providerami
  • context.select — punktowa subskrypcja konkretnego pola modelu
  • StreamProvider i FutureProvider — wbudowane wsparcie dla danych reaktywnych i asynchronicznych

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również