Skip to content

dinkar1708/flutter_riverpod_template

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

flutter_riverpod_template

A new Flutter template project using the Riverpod library for state management.

Table of Contents

  1. Setup
  2. Guide to Run Code
  3. API Used
  4. Features
  5. Run Configuration Guide
  6. Coding Guide
  7. Release Guide
  8. APIs
  9. Riverpod Library Guide
  10. FAQ
  11. DO/DON'T
  12. TODOs

Setup

Prerequisites

  • Flutter SDK installed - Current tested flutter SDK 3.19.6
  • Android Studio / VS Code installed
  • Emulator / Simulator / Physical device for testing

Installation

  • Clone project
  • Run flutter pub get
  • Generating Code - To generate the necessary code, use the following commands:

One-time generation:

dart run build_runner build --delete-conflicting-outputs

Continuous watch:

dart run build_runner watch --delete-conflicting-outputs

Guide to Run Code

Run configuration configuration using .launch.json file

Mock data

  • Guide: Mocking Providers
  • Use mock run configuration to use mock/hard-coded data TODO
  • Use actual configuration run actual API data

Final after running the app

  • Test iOS device of all configurations dev and prod

  • iOS Test
  • Same as iOS Test for Android device

API Used in the project

GitHub API:

  • Base URL: https://api.github.com/

Repositories by User Name

  1. Users:
    • User Repositories: https://api.github.com/users/:username/repos
  • Endpoint: users/dinkar1708/repos?per_page=3

Features

Home Page

  • Navigation to feature pages
  • Home Page

Feature: Navigation

Feature: Counter without API

API-TYPE: No API

Requirement: Maintain widget local state only without network operation

How to Use Riverpod in This Case: Use flutter hooks HookWidget widget

  1. Define parent class:
// see parent class

@RoutePage()
class CounterPage extends HookWidget {
  1. Use notifier provider on UI
// define
    final counterState = useState(0);
// use variable
 Text(
              'Value ${counterState.value}',
              style: AppTextStyle.labelMedium
                  .copyWith(color: context.color.textPrimary),
            ),
// modify variable
 onPressed: () {
          counterState.value = counterState.value + 1;
        },
  • Counter Feature

Feature: User GitHub Repository List

API-TYPE: GET

Requirement: Fetch Data from Network

How to Use Riverpod in This Case: User Future Provider - https://riverpod.dev/docs/providers/future_provider

  1. Define a future and perform a network call:
@riverpod
class RepositoryListNotifier extends _$RepositoryListNotifier {
// below is the future provider
  @override
  Future<List<RepositoryListModel>> build() async => await ref
      .read(userRepositoryProvider)
      .getRepositories(userName, pageSize);
}
  1. Define parent class:
// see parent class 
class RepositoryListPage extends ConsumerStatefulWidget {
  1. Use notifier provider on UI
final repositoryListAsync = ref.watch(repositoryListNotifierProvider);
return switch (repositoryListAsync) {
  AsyncError(:final error) =>
    SliverToBoxAdapter(child: Text('Error $error')),
  AsyncData(:final value) => _buildListView(value),
  _ => const SliverToBoxAdapter(child: Center(child: Text('Loading...'))),
};
  • Repository List

Feature: Search Users and Handle widget local state

API-TYPE: GET

Requirement: Fetch Data from Network and handle widget local state(clear button in search box)

How to Use Riverpod in This Case: User Future Provider with hooks - https://riverpod.dev/docs/essentials/side_effects#going-further-showing-a-spinner--error-handling

  1. Define a future and perform a network call:
// TODO later
  1. Define parent class:
// see parent class is StatefulHookConsumerWidget which is special using via 'hooks_riverpod' library
// task 1 can use useState     final isSearchingNotifier = useState(false);

// task 2 can do read and watch -     final usersListAsync = ref.watch(usersNotifierProviderProvider);

class UsersPage extends StatefulHookConsumerWidget {
  1. Use notifier provider on UI

// use widget local state variable

final isSearchingNotifier = useState(false);
// Change the value
        onChanged: (value) {
          isSearchingNotifier.value = true;
        },
// load user list data
    final usersListAsync = ref.watch(usersNotifierProviderProvider);
    return switch (usersListAsync) {
      AsyncError(:final error) => SliverToBoxAdapter(
          child: SliverToBoxAdapter(child: Text('Error $error'))),
      AsyncData(:final value) => _buildListView(value),
      _ => const SliverToBoxAdapter(child: Center(child: Text('Loading...'))),
    };
  • Search Users

Feature: Login

API-TYPE: POST

Requirement: Send data to network

How to Use Riverpod in This Case:

To use the Flutter Future Notifier Provider, we can follow the guidelines provided in the Riverpod documentation. For a more comprehensive guide on handling side effects, such as showing a spinner and error handling, refer to this section of the Riverpod documentation. However, for simplicity and to maintain clean code, you can handle error messages and loading states using the FutureProvider in Riverpod.

  1. Define a future and perform a network call:
@riverpod
class LoginNotifier extends _$LoginNotifier {
  @override
  Future<LoginStateModel> build() async {
    debugPrint('login initial state....');
    return Future.value(const LoginStateModel());
  }
  1. Define parent class:
// see parent class 
class LoginPage extends ConsumerStatefulWidget {
  1. Use notifier provider on UI // use to handle progress indicator
    // watch all the times
    final loginState = ref.watch(loginNotifierProvider);
    // use on UI with condition
          child: loginState.value?.apiResultState == APIResultState.loading
          ? const CircularProgressIndicator()
          : const Text('Login'),

// use to show error message

      // use error message on UI
        const SizedBox(
          height: 100,
        ),
        if (loginState.value?.errorMessage != null)
          Text(loginState.value!.errorMessage),

// use to do network request

// call api and handle error such as snack bar or alert etc.
         loginNotifier.login(loginRequestModel).then((loginStateModel) => {
              if (loginStateModel.apiResultState == APIResultState.result &&
                  loginStateModel.loginResponseModel != null)
                {
                  showSnackBar(context,
                      'Login success ${loginStateModel.loginResponseModel!.userName}'),
                  context.router.replaceAll([HomeRoute(title: 'Home')]),
                }
              else
                {
                  // show error message as snack bar or dailog anything
                  showSnackBar(
                      context, 'Login failed ${loginStateModel.errorMessage}'),
                }
            });

Loading State after the button is clicked

  • Loading State

Loaded State after the API call is done

  • Loaded State

Feature TODO: Complex Widget local state

API-TYPE: No api

Requirement: Manage complext widget local state

How to Use Riverpod in This Case: State notifier provider https://riverpod.dev/docs/providers/state_notifier_provider

  1. Define state model class StateModel {

}

  1. Define a state notifier and define initial state:
@riverpod
class ABCNotifier extends _$ABCNotifier {
  @override
  // note here not using future
  StateModel build() async {
    return StateModel();
  }
  1. Use notifier provider on UI
//

Feature TODO: Combination of POST, GET, or Widget Local State

Define the parent StatefulHookConsumerWidget, which allows you to use useState and ref to access the provider in the desired manner.

Examples:

  • POST + Local State: Follow the login example, but define the parent StatefulHookConsumerWidget to use useState.

  • POST + GET: Follow the login example and the repository list example.

  • POST + GET + Widget Local State: Follow the login example, the repository list example, and define the parent as StatefulHookConsumerWidget to use useState.

Run Configuration Guide

Guide link

This section provides guidance on setting up flavors, run configurations, and build modes for iOS.

Flavors/ Run Configuration and Build Mode Guide

Reference flavors guide - YouTube Video Only the "dev" and "prod" environments follow the same steps to create a staging environment if needed. Additionally, update the same code in the main folder. Here, too, in the app_config.dart, add a new variable named staging to the AppEnvironment

A. iOS - Add Flavor (Schema) using Xcode

  • **Add/Edit Schema:

** Add/Edit Schema

  • Change Schema Build Configuration etc.: Change Schema Build Configuration

  • Manage Schema Page: Manage Schema Page

  • Configuration: In the image below, all build modes (Debug, Profile, and Release) have been shown for dev and prod flavors (Flavors). Configuration

  • Fix Main File Path: Go to Runner -> Target -> Build Settings -> All, search for flutter_target, specify value for each main file. Fix Main File Path

  • Fix Display App Name: Go to Runner -> Target -> Build Settings -> All, search for product name, specify value for each main file. Fix Display App Name

  • Fix Bundle Identifier: Go to Runner -> Signing & Capability -> Build Settings -> All. Fix Bundle Identifier

B. Android - Add Flavor (Schema)

  • Add in build.gradle (app) Add in build.gradle

C. Use Flavor

Run Configuration Setting Using VS Code

Using "dev" and "prod" flavors as examples, with "Debug" and "Release" modes:

Run configuration for dev and prod flavors

Run configuration for dev and prod flavors

Coding Guide

Riverpod Guide

For detailed information on how Riverpod is used in this project, please refer to the Features section, where each feature is accompanied by a guide based on Riverpod's functionality and requirements.

App navigation

  • follow official documentation Auto Route
  • must run Generating Code to generate route

Package used

This Flutter project utilizes the following packages:

  • Riverpod - State management
  • Retrofit - API call
  • Dio - HTTP client
  • Build Runner - Code generation
  • Freezed - Code generation for models
  • Freezed Annotations - Annotations for code generation

Guide to inspect widget

Start widget inspection. See below picture

Start widget inspection

Select mode -> Click button 'Toggle select widget mode'. See below picture

Toggle select widget mode

Select widgets - Explore the widget tree to view details. Refer to the following information about the tree:. See below picture

Select widgets

End widget inspection. See below picture

End widget inspection

Release Guide

Guide CI/CD For detailed guidance refer to: GitHub Actions Quickstart

Github Actions (CI/CD)

First-time User? Follow these steps:

  1. Visit flutter-actions/setup-flutter and copy the provided basic version. Paste it into any file, give it a name, commit, push, and create a pull request. This action will automatically begin running.

  2. If you have an extra step, add it to the .yml file:

    - name: Generate code
      run: dart run build_runner build --delete-conflicting-outputs
  3. Now, check if the automatic CI running has passed.

How to Use This Repository's .yml File:

  • Simply copy and paste the build.yml file into your repository under .github/workflows/build.yml, ensuring to specify the correct version of the Flutter SDK, and it will automatically start building.

Riverpod Library Guide

  • Main library
  flutter_riverpod: ^2.2.0
  riverpod_annotation: ^2.3.3
  • Use flutter hooks to manage state of variables
flutter_hooks
  • StatefulHookConsumerWidget which offers HookWidget and ConsumerStatefulWidget both features.
hooks_riverpod

APIs

For example API usage, refer to the list below: For example API usage, refer to the API List.

FAQ

  • Can use the hooks_riverpod package, StatefulHookConsumerWidget which offers HookWidget and ConsumerStatefulWidget both features.

  • All pages must be suffixed by 'Page' to generate auto router automatically Example Correct - HomePage // at the end must add Page Wrong - HomeView, HomeWidget, HomeStatefullWidget

  • To resolve compile errors, follow these steps:

  1. Ensure that generator dependencies are added to pubspec.yaml (retrofit_generator, riverpod_generator).
  2. Manually delete generated files (.g and .freezed.dart) before running the build runner commands.
  3. Fix compile issues (except for generated syntax) before running build runner commands again.

DO/DON'T

  1. Use hooks for storing widget local state

TODOs

  • Fix command line run -> flutter run --flavor development
    • Target file "lib/main.dart" not found.
  • Run using Android Studio configurations
    • Able to run using Android Studio
  • Implement GitHub APIs using different Riverpod library usages