Bloc VS Riverpod

Bloc vs Riverpod: Welches State Management System passt zu deiner App.

Die Entwicklung von Apps mit Flutter hat in den letzten Jahren an Popularität gewonnen, da dieses Framework eine effiziente Erstellung von plattformübergreifenden Apps mit nativem Feeling ermöglicht. Ein entscheidender Aspekt bei der Entwicklung solcher Anwendungen ist das State Management, das für die Verwaltung des Zustands der App sorgt. In diesem Zusammenhang haben sich besonders zwei Lösungen hervorgetan: Flutter Bloc und Flutter Riverpod

Dieser Artikel beleuchtet die Unterschiede, Funktionsweise und zeigt auch die Unterschiede im Code, um dir eine Orientierungshilfe bei der Auswahl des geeigneten State Management Systems zu bieten.

Was ist Flutter Bloc?

Flutter Bloc ist ein beliebtes State Management Paket, das auf den Prinzipien von Events und States basiert. Entwickelt wurde es, um eine klare Trennung zwischen der Geschäftslogik und der Präsentationsebene zu fördern. Durch das Bloc Muster können Entwickler reaktive Anwendungen erstellen, die effizient auf Benutzereingaben oder andere Ereignisse reagieren, indem Zustandsänderungen als einfache Ereignisse behandelt werden.

Funktionsweise

Im Kern des Bloc Musters stehen drei Hauptkomponenten: Event, State und Bloc Controller selbst. 

Events sind Aktionen, die von der Benutzeroberfläche ausgelöst werden und eine Änderung des Zustands anfordern. Das kann zum Beispiel ein Button sein, der eine Anfrage an den Backend Server triggert. 

Der State repräsentiert den Teil des App-Zustands, den der Bloc verwaltet. Zum Beispiel Loading Status, Succes Status und Error Status.

Der Bloc Controller verarbeitet eingehende Events, kann den Status des Blocs verändern , die dann an die UI zurückgegeben werden, um eine entsprechende Reaktion oder Aktualisierung zu bewirken

Flutter Bloc Prizip

Die Abbildung oben zeigt einen typischen Workflow für eine Bloc Implementierung. In der UI wird ein Event getriggert, dieses Event wird im Bloc Controller verarbeitet und ändert den Status der UI. 

Wenn du mehr über Bloc erfahren möchtest, dann schau dir meinen Beitrag dazu an oder schau auf meinem Youtube Kanal vorbei.

Was ist Flutter Riverpod?

Flutter Riverpod ist eine neuere und flexiblere Alternative zu traditionellen State-Management-Lösungen in Flutter, die von der Community schnell angenommen wurde. Es baut auf den Stärken von Provider auf und adressiert gleichzeitig einige der Einschränkungen, die Entwickler bei der Verwendung von Provider und sogar Bloc erlebt haben.

Funktionsweise

Du kennst bestimmt diese Abbildung aus dem Blog von Andrea. Der wesentliche Unterschied zwischen Provider und Riverpod ist, das Riverpod alle Provider dem ganzen Widgetbaum zur Verfügung stellen. 

Also hat somit die ganze App auf alle Provider zugriff. Und somit kann der Fehler, dass der Provider nicht gefunden wurde, nicht eintreten. 

Flutter Riverpod: Übersicht des Provider Prinzip | Flutter Bloc vs Riverpod

Dabei wird im Wesentlichen zwischen dem Leseprovider, der nur Daten lesen und zur Verfügung stellen kann, und dem Schreibprovider, der auch veränderbare Daten enthalten kann, unterschieden.

In meinem Blogpost, was eigentlich auch ein kleines Riverpod Tutorial werden sollte, habe ich ein wenig über Riverpod abgelästert, aber ich habe mich nochmal etwas damit auseinandergesetzt und muss sagen, dass sich meine Meinung zu Riverpod etwas verbessert hat. Gerade weil durch die neuen Annotationen man nicht mehr so viel Boilerplate Code schreiben muss. Trotzdem bevorzuge ich meist immer noch Flutter Bloc.

Vergleich der Kernkonzepte

Mit Riverpod stehen dir alle Provider gleichzeitig zur Verfügung. Das heißt, dass du in jedem kleinen Widget, das du implementierst, theoretisch alle Zustände ändern könntest. Ob das nun ein Vorteil oder ein Nachteil ist, das ist ja irgendwie Auslegungssache. Für mich ist es ein Nachteil, da ich davon ein Fan bin, nur dann Daten zur Verfügung zu stellen, wenn ich sie wirklich brauche.

Im Gegenzug steht hier Bloc, das nur dann verwendet wird, wenn es auch initialisiert wird. Das heißt, wenn der Bloc nicht benötigt wird, dann wird er auch nicht erstellt. Für mich überwiegt damit das Kernkonzept von Bloc.

Vergleich der Lernkurve

Flutter Bloc hat mich damals einige Wochen beschäftigt, bis ich es komplett durchblickt habe. Aber wenn man einmal das Prinzip verstanden hat, sollte man keine Probleme haben, auch größere und komplexere Blocs zu schreiben.

Auch bei Riverpod musste ich einiges an Zeit investieren, um es zu verstehen. Aber im Vergleich zu Bloc war es hier noch einmal ein bisschen mehr. Außerdem hatte ich mehr Probleme, die Arbeitsweise von Riverpod zu verstehen. Ich gebe zu, dass ich bis heute nicht alle Facetten von Riverpod verstanden habe.

Vergleich im Code

Als Beispiel hab ich ein Teil meiner Wetterapp in Bloc und in Riverpod abgebildet. Das Beispiel startet eine Abfrage an den Server, um die aktuellen Wetterdaten anzuzeigen. Dabei gibt es einen Ladebildschirm während der Abfrage und eine Fehleranzeige, wenn etwas schief läuft.

Beispiel mit Riverpod

Die vereinfachte Abbildung zeigt nur das Widget und den dazugehörigen Provider. Es fehlt hier noch der komplette Data Layer, den man über den weatherRepositoryProvider bekommt.

weather_overview_screen.dart

				
					class WeatherOverviewScreen extends ConsumerStatefulWidget {
  const WeatherOverviewScreen({super.key});

  @override
  ConsumerState createState() => _WeatherOverviewScreenState();
}

class _WeatherOverviewScreenState extends ConsumerState<WeatherOverviewScreen> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ref.watch(weatherFutureProvider).when(
        loading: () {
          return const CircularProgressIndicator();
        },
        error: (error, stack) {
          return Text(error.toString());
        },
        data: (weather) {
          return Text(weather.temperature.toString());
        },
      ),
    );
  }
}

				
			

weather_future_provider.dart

				
					final weatherFutureProvider = FutureProvider.autoDispose<Weather>((ref) {
  // get repository from the provider below
  final weatherRepository = ref.watch(weatherRepositoryProvider);
  // call method that returns a Future<Weather>
  return weatherRepository.getForecast(
    language: 'de',
    latitude: 50,
    longitude: 10,
  );
});

				
			

weather_repository.dart

				
					@riverpod
WeatherRepository weatherRepository(WeatherRepositoryRef ref) =>
    WeatherRepository(
      apiHandler: ref.watch(apiHandlerProvider),
    );

class WeatherRepository {
  WeatherRepository({
    required ApiHandler apiHandler,
  }) : _apiHandler = apiHandler;

  final ApiHandler _apiHandler;

  Future<Weather> getForecast({
    required double latitude,
    required double longitude,
    required String language,
  }) async {
    final response = await _apiHandler.get(
      ApiRoutes.weather,
      queryParameters: {
        'latitude': latitude,
        'longitude': longitude,
        'language': language,
      },
    );
    return Future.value(Weather.fromJson(response.data.first));
  }
}

				
			

Beispiel mit Bloc

Auch hier gibt es nur die vereinfachte Version. Was man hier schon auf den ersten Blick sieht, ist, dass der Bloc Code noch einige Zeilen mehr hat.

weather_overview_screen.dart

				
					class WeatherOverviewScreen extends StatefulWidget {
  const WeatherOverviewScreen({super.key});

  @override
  State<WeatherOverviewScreen> createState() => _WeatherOverviewScreenState();
}

class _WeatherOverviewScreenState extends State<WeatherOverviewScreen> {
  late WeatherOverviewBloc bloc;

  @override
  void initState() {
    bloc = WeatherOverviewBloc(
      weatherRepository: RepositoryProvider.of<WeatherRepository>(context),
    );
    bloc.add(const WeatherOverviewFetchedWeatheredEvent());
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<WeatherOverviewBloc, WeatherOverviewState>(
      bloc: bloc,
      listener: (context, state) {
        if (state is WeatherOverviewFetchedWeatherFailure) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text(state.error),
            ),
          );
        }
      },
      builder: (context, state) {
        if (state is WeatherOverviewFetchedWeatherInProgress) {
          return const Center(
            child: CircularProgressIndicator(),
          );
        }
        if (state is WeatherOverviewFetchedWeatherSuccess) {
          return Text(state.weather.temperature.toString());
        }
        return const SizedBox.shrink();
      },
    );
  }
}

				
			

weather_overview_bloc.dart

				
					class WeatherOverviewBloc
    extends Bloc<WeatherOverviewEvent, WeatherOverviewState> {
  WeatherOverviewBloc({
    required WeatherRepository weatherRepository,
  })  : _weatherRepository = weatherRepository,
        super(WeatherOverviewInitial()) {
    on<WeatherOverviewFetchedWeatheredEvent>(
      _onWeatherOverviewFetchedWeatheredEvent,
    );
  }

  final WeatherRepository _weatherRepository;

  Future<FutureOr<void>> _onWeatherOverviewFetchedWeatheredEvent(
    WeatherOverviewFetchedWeatheredEvent event,
    Emitter<WeatherOverviewState> emit,
  ) async {
    try {
      emit(WeatherOverviewFetchedWeatherInProgress());
      final weather = await _weatherRepository.getForecast(
        language: 'de',
        latitude: 50,
        longitude: 10,
      );
      emit(WeatherOverviewFetchedWeatherSuccess(weather: weather));
    } catch (error) {
      emit(WeatherOverviewFetchedWeatherFailure(error: error.toString()));
    }
  }
}

				
			

weather_repository.dart

				
					class WeatherRepository {
  WeatherRepository({
    required this.weatherProvider,
  });

  final WeatherProvider weatherProvider;

  Future<Weather> getForecast({
    required double latitude,
    required double longitude,
    required String language,
  }) async {
    return weatherProvider.getForecastWeather(
      latitude: latitude,
      longitude: longitude,
      language: language,
    );
  }
}

				
			

Fazit Bloc vs Riverpod

Beide sind sehr beliebte State Management Systeme und haben eine große Fangemeinde. Manchmal fühlt es sich an wie der ewige Kampf zwischen Windows und Apple.

Falls du weniger Code schreiben möchtest und jederzeit Zugang zu allen Daten haben willst, dann ist Riverpod eine gute Wahl. Falls du jedoch nur den Bereich deines Codes mit State Management ausstatten möchtest, der es auch gerade benötigt, und kein Problem mit etwas mehr Code hast, dann ist Flutter Bloc eine gute Wahl.

Generell machst du mit beiden keine schlechte Wahl. Noch besser wäre es, wenn du beide Systeme ausprobierst, um auch für deine Kunden oder in deinem Job für beides gewappnet zu sein.

Posted in State Management System, Allgemein, Flutter/DartTags:
Write a comment