Code Monkey home page Code Monkey logo

flutter_riverpod_boilerplate_project's Introduction

Flutter Riverpod Boilerplate Project

This is a boilerplate flutter project created using Riverpod and GetIt. It currently support only Mobile (Tablet and Phone) with both production and staging environment capabilities. You can clone the project using the link below:

Getting Started

The Boilerplate contains the minimal implementation required to create a new project. This repository is preloaded with some basic app architecture that can be expanded to to fit in larger project. The purpose of this repository is to help reduce setup and development time and avoid re-writing same code pattern for every app to be created. If you have any suggestions or improvements, feel free

Installation

Please note: This repository requires Flutter to be installed to your development machine. After that is done, follow the below steps.

Step 1:

Download or clone this repo by using the link below:

https://github.com/peterewanfo/flutter_riverpod_boilerplate_project.git

Step 2:

Go to project root and execute the following command in console to get the required dependencies:

flutter pub get 

Packages Used

This repository makes use of the following pub packages:

Package Version Usage
Hooks Riverpod ^1.0.3 State Management
Flutter Hooks ^0.18.2+1 Increase the code-sharing between widgets by removing duplicates.
Flutter Lints ^1.0.0 To encourage good coding practices.
Flutter Screenutil ^5.0.0+2 For adapting screen and font size
GetIt ^7.1.3 For accessing service objects/Views/AppModels
Connectivity Plus ^2.3.9 To discover network connectivity
Dartz ^0.10.1 For easy and safe error handling with functional programming stule in Dart
Shared Preference ^6.0.0 To store data as key/value pairs
Flutter Secure Storage ^2.0.7 To store data in secure storage
Flutter dotenv ^5.0.2 To manage/read .env files
Pretty Dio Logger ^1.1.1 A Dio interceptor that logs network calls in a pretty, easy to read format
Dio ^4.0.4 Http Client for Dart
Another Flushbar ^1.12.29 To substitute toasts and snackbars and introduce more customization when notifying your user.
Change App Package Name ^1.1.0 To change app package name with single command. It makes the process very easy and fast.

Removing unwanted packages

If any package is not needed, then removing it from pubspec.yaml file as well as all imports and uses should be enough.

Changing the package and app name

use the Change App Package Name the package is already included in this boilerplate project. Simply run this command

flutter pub run change_app_package_name:main com.new.package.name

where "com.new.package.name" is your desired backage name for the project. For other information on how to do this, kindly visit this Stackoverflow issues

Boilerplate Features:

  • Splash
  • Login
  • Signup
  • Home
  • Routing
  • Dio
  • Database
  • Riverpod
  • Validation
  • Loggin
  • Dependency Injection
  • Connectivity

Up-Coming Feature:

  • Widget and Unit Test Support
  • Robust Example project project

Folder Structure

app-base-directory/
| - android
| - assets
| - build
| - ios
| - lib
| - test
| - .env
| - .env_prod

Assets

This contains static image resources and fonts used in the application
This is what the assets file structure looks like

assets/
| - fonts
| - images

Lib

Let's see a detailed view on the lib folder

lib/
| - data
| - handlers
| - models
| - presentation
| - utils
| - app.dart
| - main.prod.dart
| - main.dart

Here is a brief description of what is contained in each folder

1: data - includes directories for network calls and shared preferences
2: handlers - contains navigation handler and dialog manager/handler for managing dialogs and application navigation at a global level.
3: models - contains data models of your application
4: presentation - contains your application UI(Views), ViewModels, custom styles, defined routes and custom designed widgets.
5: app.dart - in here we load the application and set flag for staging or production based on the current active app flavour
6. main.dart - this is the app main lancher and uses the staging app flavour
7. main.prod.dart - this is the production main lancher and uses the production app flavour

for more explanation on app flavour and it is setup in a project, checkout this youtube video or this article

Now let's dive deep exhausively into each folder and see what each entails.

Data

This contains the data layer of your project. It is home to all your application business logic. From here, your application gets all resources it needs to best serve the user, this includes network resources (api calls) and local resources (sharepreferences or secure storage) as prefered. This is what the data file structure looks like

data/
| - config
    | - base_api.dart
| - services
    | - local
        __local.dart
        | - implementations
            | - secure_storage_impl.dart
            | - shared_preference_impl.dart
        | - repositories
            | - secure_storage_repository.dart
            | - shared_preference_repository.dart
    | - remote
        __remote.dart
        | - implementations
            | - user_repository_impl.dart
        | - repositories
            | - user_repository.dart

Like you have noticed, this repository provides implementation for both Flutter secure storage and shared preference, while both does thesame task of keeping data to the device, the former encrypts data stored, the latter doesnt. You can check this article on how both works and decide which to use, like said earlier both implementations are available in this repository, ready for you to decide.

Handlers

This contains manager/handler for managing dialogs and application navigation at a global level. with this, we're making provisions to enable navigations and dialog pop-up from anywhere in our application either in views/viewmodel/custom functional methods e.t.c. Learn more about this from these amazing blog posts

handlers/
| - __handlers.dart
| - dialog_handler.dart
| - navigation_handler.dart

File naming convention to note

  1. "__folder_name.dart" In every folder, there is a file named "__folder_name.dart". The purpose of this file is to enable export multiple dart files from just one. This is extensively used in the project for example in the handlers folder above, there exist the file named __handlers.dart *sample __handlers.dart
export dialog_handler.dart;
export navigation_handler.dart;

now importing __handlers.dart also imports dialog_handler.dart and navigation_handler.dart

For more explanations and examples on how this works, checkout this article

Models

On here is where all model classes for our application is created. It can be further split into folders like api, just to differentiate model classes created.

Presentation

This contains the following sub folders custom_designs, routes, style, view_models, views. This is what the presentation file structure looks like

Here is a brief description of what is contained in each folder

1: custom_designs - This contains all custom and shared widgets.

2: routes - This contains two files routes.dart and route_generator.dart. The former contains all routes to your application while the latter, contains custom route generator callback for all routes in your application. The structure looks like this

presentation/
| - routes
    | - __routes.dart
    | - route_generator.dart
    | - routes.dart

*Sample routes.dart

class Routes {
  static const splashScreenView = "/splashScreenView";
  static const signupView = "/signupView";
  static const loginView = "/loginView";
}

*Sample route_generator.dart

class RouteGenerator {
  static Route<dynamic> generateRoute(RouteSettings settings) {
    final args = settings.arguments;

    switch (settings.name) {
      case Routes.splash_screen:
        return _getPageRoute(SplashScreen(), settings);
      default:
        return _getPageRoute(_errorPage());
    }

    static CupertinoPageRoute _getPageRoute(
        Widget child, [
        RouteSettings settings = const RouteSettings(),
        bool? isfullScreenDialog = false,
        ]
    ) => CupertinoPageRoute(
            builder: (context) => child,
            fullscreenDialog: isfullScreenDialog ?? false,
            settings: RouteSettings(
            name: settings.name,
            arguments: settings.arguments,
            ),
        );

    static Widget _errorPage({String message = "Error! Page not found"}) =>
      kDebugMode
          ? Scaffold(
              appBar: AppBar(
                  title: const Text(
                'Page not found',
                style: TextStyle(color: Colors.red),
              )),
              body: Center(
                child: Text(
                  message,
                  style: const TextStyle(color: Colors.red),
                ),
              ),
            )
          : const SizedBox();

3: style - This contains application styling including app_theme.dart, custom_colors.dart The structure looks like this

presentation/
| - style
    | - __style.dart
    | - app_theme.dart
    | - custom_colors.dart
    | - custom_style.dart

4: view_models - In reference to an amazing article by Jitesh Mohite on "Flutter: MVVM Architecture". ViewModel is the mediator between View and Model. It's from here we handle all user events, fetch data required and notify the view. If you wish to learn more about MVVM, you can checkout this amazing article "Flutter: MVVM Architecture"

5: views - This directory contains all the UI of your application.

Handling Mobile and Tablet view compatibility

For View responsiveness across mobile and tablet, we use Layout builder to determine which view version to launch based on device width. contained in this boilerplate is a custom layout builder to handle this functionality for all views.

  • sample code - responsive_laout.dart
import 'package:flutter/material.dart';

class ResponsiveLayout extends StatelessWidget {
  final Widget? tablet;
  final Widget mobile;

  const ResponsiveLayout({
    Key? key,
    required this.mobile,
    this.tablet,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (_, constraint) {
        if (constraint.maxWidth <= 488) return mobile;
        return tablet ?? mobile;
      },
    );
  }
}
  • To use this functionality, other views can then be structure like below.
views/
| - login
    | - login_mobile_view.dart
    | - login_tablet_view.dart
    | - login_view.dart

In login_view.dart, we import responsive_laout.dart and pass as parameters the different views( login_mobile_view.dart and login_tablet_view.dart )

  • sample code - login_view.dart
import 'package:flutter/material.dart';
import 'package:boilerplate_project/presentation/custom_designs/responsive_layout.dart';
import 'package:boilerplate_project/presentation/views/login_mobile_view.dart';
import 'package:boilerplate_project/presentation/views/login_tablet_view.dart';

class LoginView extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return ResponsiveLayout(
      mobile: LoginMobileView(),
      tablet: LoginTabletView(),
    );
  }
}

Utils

This direcctory is used as the helper folder of the application. It contains the following files:

  1. api_endpoints.dart: Here, you keep all api endpoints called in your application. *sample code - api_endpoints.dart
class ApiEndpoints {
  static const login = "/login";
  static const signUp = "/sign_up";
}
  1. app_strings.dart: Here we keep string constants used in your application. It could either be keys to shared preferences e.t.c *sample code - app_strings.dart
class AppStrings {
  AppStrings._();

  //SHARE PREFERENCE STRINGS
  static const accessTokenPref = "access_token_pref";
  static const refreshTokenPref = "refresh_token_pref";

}
  1. connection_status.dart: This helper file contains class that tells if your application has internet connection. *sample code - connection_status.dart
import 'package:connectivity/connectivity.dart';

class ConnectionStatus {
  static Future<bool> isConnected() async {
    var connectionResult = await (Connectivity().checkConnectivity());

    if (connectionResult == ConnectivityResult.mobile)
      return true;
    else if (connectionResult == ConnectivityResult.wifi)
      return true;
    else {
      return false;
    }
  }
}
  1. enums.dart: This file contains all enums used in your applications.

  2. extensions.dart: This file contains extensions to add functionality to widgets and libraries preventing code repitition.

  3. validators.dart: This file contain custom form validators used in your application.

  4. locator.dart: This file contains the configuration of getIt service locator used in this boilerplate.

  • More Explanation - In here we register shared preferences and custom app flavour as singleton and other classes used as lazy singleton. The purpose of lazy singleton is for initializing resources at the time of the first request instead at the time of declaration.

*sample code - locator.dart

import 'package:get_it/get_it.dart';
import 'package:shared_preferences/shared_preferences.dart';

GetIt locator = GetIt.instance;

Future<void> setupLocator({
  String baseApi = "",
  AppFlavor flavor = AppFlavor.debug,
}) async {
  locator.registerSingleton<AppFlavor>(flavor);

  final sharedPreferences = await SharedPreferences.getInstance();
  locator.registerSingleton(sharedPreferences);

  //Local storage
  locator.registerLazySingleton<SecureStorage>(
    () => SecureStorageImpl(),
  );

  locator.registerLazySingleton<LocalCache>(
    () => LocalCacheImpl(
      sharedPreferences: locator(),
      storage: locator(),
    ),
  );
}
  1. logger.dart: From here, we toggle logs 'on' when on debug mode and off when in production

*sample code - logger.dart

import 'dart:developer' as dev;

class AppLogger {
  AppLogger._();

  static bool _showLogs = false;
  static bool get showLogs => _showLogs;

  static void setLogger({required bool showLogs}) {
    _showLogs = showLogs;
  }

  static void log(Object? e) {
    if (_showLogs) dev.log("$e");
  }
}
  1. env_config.dart: This contains method to load different environment variables used in your application. These environment file (.env and .env_prod) is located in the applicationn project folder. These files are added to gitignore so you should create them manually after cloning this boilerplate project.
  • sample code - env_config.dart
import 'package:boilerplate_project/utils/logger.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';

Future<void> loadEnvFile({String path = ".env"}) async {
  try {
    await dotenv.load(fileName: path);
  } catch (e) {
    AppLogger.log(e);
  }
}

Future<void> loadProdEnvFile({String path = ".env_prod"}) async {
  try {
    await dotenv.load(fileName: path);
  } catch (e) {
    AppLogger.log(e);
  }
}

Contribution

If you wish to contribute to this boilerplate project, please feel free to submit an issue and/or pull request.

Thanks for your time.

flutter_riverpod_boilerplate_project's People

Contributors

peterewanfo avatar

Stargazers

Arfi Antasofa avatar Thomas Hoang avatar  avatar Ahmad Yunus Afghoni avatar Sandesh Raj Rijal avatar  avatar Florian avatar

Watchers

Florian avatar  avatar

flutter_riverpod_boilerplate_project's Issues

Clean install run fail

Hi - looks like a great project. I tried to run it after flutter pub get and it fails with the below (I have no problem building other projects. I did add a .env (it is blank) - could you send a example env please as maybe that is the issue?

╭─ ♥ 13:03 |       flutter_riverpod_boilerplate_project
╰─ flutter run lib/main.dart
Using hardware rendering with device Android SDK built for x86. If you notice graphics artifacts, consider enabling software rendering with "--enable-software-rendering".
Launching lib/main.dart on Android SDK built for x86 in debug mode...
Warning: Mapping new ns http://schemas.android.com/repository/android/common/02 to old ns http://schemas.android.com/repository/android/common/01
Warning: Mapping new ns http://schemas.android.com/repository/android/generic/02 to old ns http://schemas.android.com/repository/android/generic/01
Warning: Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/02 to old ns http://schemas.android.com/sdk/android/repo/addon2/01
Warning: Mapping new ns http://schemas.android.com/sdk/android/repo/addon2/03 to old ns http://schemas.android.com/sdk/android/repo/addon2/01
Warning: Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/02 to old ns http://schemas.android.com/sdk/android/repo/repository2/01
Warning: Mapping new ns http://schemas.android.com/sdk/android/repo/repository2/03 to old ns http://schemas.android.com/sdk/android/repo/repository2/01
Warning: Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/03 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01
Warning: Mapping new ns http://schemas.android.com/sdk/android/repo/sys-img2/02 to old ns http://schemas.android.com/sdk/android/repo/sys-img2/01
Warning: unexpected element (uri:"", local:"extension-level"). Expected elements are <{}codename>,<{}layoutlib>,<{}api-level>
Warning: unexpected element (uri:"", local:"base-extension"). Expected elements are <{}codename>,<{}layoutlib>,<{}api-level>
../../../.pub-cache/hosted/pub.dev/flutter_screenutil-5.5.4/lib/src/screen_util.dart:58:5: Error: Type 'FlutterWindow' not found.
FlutterWindow? window,
^^^^^^^^^^^^^
../../../.pub-cache/hosted/pub.dev/flutter_screenutil-5.5.4/lib/src/screen_util.dart:58:5: Error: 'FlutterWindow' isn't a type.
FlutterWindow? window,
^^^^^^^^^^^^^
lib/presentation/style/app_theme.dart:11:7: Error: No named parameter with the name 'brightness'.
brightness: Brightness.light,
^^^^^^^^^^
../../../snap/flutter/common/flutter/packages/flutter/lib/src/material/app_bar_theme.dart:29:9: Context: Found this candidate, but the arguments don't match.
const AppBarTheme({
^^^^^^^^^^^
Target kernel_snapshot failed: Exception

FAILURE: Build failed with an exception.

  • Where:
    Script '/home/wayne/snap/flutter/common/flutter/packages/flutter_tools/gradle/flutter.gradle' line: 1201

  • What went wrong:
    Execution failed for task ':app:compileFlutterBuildDebug'.

Process 'command '/home/wayne/snap/flutter/common/flutter/bin/flutter'' finished with non-zero exit value 1

  • Try:

Run with --stacktrace option to get the stack trace.
Run with --info or --debug option to get more log output.
Run with --scan to get full insights.

BUILD FAILED in 2s
Running Gradle task 'assembleDebug'... 3.1s
Exception: Gradle task assembleDebug failed with exit code 1 ```

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.