Flutter Riverpod Future Provider

Created At: 2023-08-09 00:25:42 Updated At: 2023-09-24 08:43:08

Flutter Riverpod Future Provider is just like regular Provider except that it returns a Future type. By saying, you should know that, we should use FutureProvider for network request or database operation including local storage. FutureProviders are great for where you load data just only once. It is meant work instead of FutureBuilder widget.

FutureProviders can not auto detect data changes unless you refresh them.

Riverpod 2.0 does not use FutureProvider directly, rather it uses Future or FutureOr 

Below is the example for network request.

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'main.g.dart';

@riverpod
Future<String> futureProviderString (FutureProviderStringRef ref) async {
  return "";
}

You see we return String but as Future which also means async and await. With the FutureProvider, the UI can deal with this synchronously. The FutureProvider will let the Widgets know when the data is ready or when the call failed. See an actual example

import 'dart:convert';

import 'package:http/http.dart' as http;
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'future_screen_controller.g.dart';

@riverpod
Future<String> futureScreenController(FutureScreenControllerRef ref) async {
  final response =
      await http.get(Uri.parse('https://random-word-api.herokuapp.com/word'));

  if (response.statusCode == 200) {
    final jsonResponse = jsonDecode(response.body);
    final randomWord = jsonResponse[0];

    return Future.delayed(const Duration(seconds: 1), () => randomWord);
  } else {
    print('Request failed with status: ${response.statusCode}.');
  }
  return 'ERROR';
}

Let's take a look at the Providers that is auto generated. That Provider lives in main.g.dart in our case. Make sure that you run the below command

flutter pub run build_runner watch --delete-conflicting-outputs

Here on number one represents the method name that we have defined. And number is the generated Provider's name. 

Number three is the type of the Provider which is FutureProvider and at the same we see AutoDispose at the beginning. So it's AutoDisposeFutureProvider.

AutoDispose is auto given but you may choose not to do like that. And the last one is the typedef which is given in the futureScreenController method.

Complete code

part 'main.g.dart';

void main() {
  runApp( ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);


  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: FutureScreen(),
    );
  }
}

class FutureScreen extends ConsumerWidget {
  const FutureScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final state = ref.watch(futureScreenControllerProvider);

    return Scaffold(
      
      backgroundColor: Colors.grey,
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            state.when(
              data: (data) => Text(
                data,
                style: const TextStyle(
                  fontSize: 40,
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                ),
              ),
              error: (error, stackTrace) => Text(
                error.toString(),
                style: const TextStyle(
                  fontSize: 40,
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                ),
              ),
              loading: () => const CircularProgressIndicator(),
            ),
          ],
        ),
      ),
    );
  }
}

@riverpod
Future<String> futureScreenController(FutureScreenControllerRef ref) async {
  final response =
  await http.get(Uri.parse('https://random-word-api.herokuapp.com/word'));

  if (response.statusCode == 200) {
    final jsonResponse = jsonDecode(response.body);
    final randomWord = jsonResponse[0];

    return Future.delayed(const Duration(seconds: 1), () => randomWord);
  } else {
    print('Request failed with status: ${response.statusCode}.');
  }
  return 'ERROR';
}

Since this is a FutureProvider, we need to use when() on the returned object of provider to get the data available from the api call.

So we use state.when() to get the data from the async provider. This means all the FutureProviders are AsyncNotifier.

Data Storage Example

Let's see another example of Riverpod FutureProvider. This time we have used code generation tool to do it. Le't see the image below

Here our class HomeUserProfile is a FutureProvider. You see the build() method that the return type is FutureOr. This returns a FutureProvider since we are retrieving data from the local storage.

 

Comment

Add Reviews