Skip to content

crossplatformkorea/flutter_boilerplate

Repository files navigation

flutter_seoul

CI Github Pages codecov

Full boilerplate project for Flutter.

Flutter

Do you need another boilerplate?

Specification

  • Localization
  • Provider package
  • Asset-related settings (Image, Icon, Color)
  • Testing settings

Dependencies

# dependencies
cupertino_icons: ^1.0.5
intl: ^0.17.0
flutter_dotenv: ^5.0.2
logger: ^1.1.0
http: ^0.13.5
freezed_annotation: ^2.2.0
json_annotation: ^4.7.0
image_picker: ^0.8.6
shared_preferences: ^2.0.15
go_router: ^6.0.3
flutter_hooks: ^0.18.5+1
hooks_riverpod: ^2.1.3
sqflite: ^2.2.4+1
path: ^1.8.2
permission_handler: ^10.2.0
flat_list: ^0.1.13

# dev_dependencies
flutter_lints: ^2.0.1
test: ^1.22.0
mockito: ^5.3.2
build_runner: ^2.3.3
flutter_native_splash: ^2.2.16
change_app_package_name: ^1.1.0
freezed: ^2.3.2
json_serializable: ^6.5.4

Getting Started

1. Install dependencies

flutter pub get

2. Create an .env file

You have to create an .env file.

Copy an .env.example file and create an .env file.

cp .env.example .env

3. Run the project

flutter run

4. (Optional) Using Git hooks

This project consists of github hooks using lefthooks

Current flutter version used is written in CI.

riverpod

Update riverpod provider with below command

flutter pub run build_runner watch -d

Navigation

go_router

Route settings

GoRouter routerConfig([String? initialLocation]) => GoRouter(
      navigatorKey: _rootNavigatorKey,
      initialLocation: initialLocation ?? GoRoutes.authSwitch.fullPath,
      routes: <RouteBase>[
        GoRoute(
          name: GoRoutes.authSwitch.name,
          path: GoRoutes.authSwitch.fullPath,
          builder: (context, state) {
            return const AuthSwitch();
          },
        ),
        GoRoute(
          name: GoRoutes.home.name,
          path: GoRoutes.home.fullPath,
          builder: (context, state) {
            return const Home();
          },
        ),
        GoRoute(
          name: GoRoutes.signIn.name,
          path: GoRoutes.signIn.fullPath,
          builder: (context, state) {
            return const SignIn();
          },
        ),
        GoRoute(
          name: GoRoutes.editProfile.name,
          path: GoRoutes.editProfile.fullPath,
          builder: (context, state) {
            var args = state.extra as EditProfileArguments;

            return EditProfile(
              title: args.title,
              person: args.person,
            );
          },
        ),
        GoRoute(
          name: GoRoutes.sample.name,
          path: GoRoutes.sample.fullPath,
          builder: (context, state) {
            return const Sample();
          },
        ),
        GoRoute(
          name: GoRoutes.result.name,
          path: GoRoutes.result.fullPath,
          builder: (context, state) {
            return const Result();
          },
        ),
      ],
    );

Navigate using go_router

context.push(GoRoutes.sample.fullPath);
context.go(GoRoutes.sample.fullPath);
context.pop(GoRoutes.sample.fullPath);

Returning values

onTap: () => context.pop(true)

onTap: () {
  final bool? result = await context.push<bool>('/page2');
  if(result ?? false)...
}

Local Database(sqflite)

Create DB

final Future<Database> database = openDatabase(
  join(await getDatabasesPath(), 'item_database.db'),
    onCreate: (db, version) {
        // 데이터베이스에 CREATE TABLE 수행
      return db.execute(
      'CREATE TABLE item(id INTEGER PRIMARY KEY ,title TEXT, content TEXT)',
    );
  },
  version: 1,
);

insert

db.insert('item', item.toJson(),
  conflictAlgorithm: ConflictAlgorithm.replace);

get

final List<Map<String, dynamic>> maps = await db.query('item');
  return List.generate(maps.length, (i) {
    return ItemModel(
      id: maps[i]['id'],
      title: maps[i]['title'],
      content: maps[i]['content'],
    );
  });

delete

db.delete(
  'item',
  where: 'id = ?',
  whereArgs: [id],
);

update

db.update(
  'item',
  item.toJson(),
  where: 'id = ?',
  whereArgs: [item.id],
);

Permission

IOS

flutter pub add permission_handler

Add permission to your Info.plist file.

IOS permissions list

Android

Add permissions to your AndroidManifest.xml file.

Android permissions list

Permission Example

var status = await Permission.camera.status;
if (status.granted) {
  /// you can do something if granted
}

if (status.isDenied) {
  /// you can do something if isDenied
}

if (status == PermissionStatus.permanentlyDenied) {
  /// If permanently denied, have to openAppSettings()
  await openAppSettings();
}

if (await Permission.location.isRestricted) {

}

Localization

The repository is localizing using the arb recommended on the official Flutter website.

To use it, you need to install the Flutter Intl extension in your VSCode or Android studio.

You can go to the link below for installation.

When Flutter Intl is installed, a dart file is automatically created when modifying the arb file.

For posture information on this, check the document on the download link. For Korean users, you can check it on this link.

How to use localization

import 'package:intl/intl.dart';
...

Text(Intl.message('appName'));

or

import 'package:flutter_seoul/generated/l10n.dart';
...

Text(S.of(context).appName);

or

import 'package:flutter_seoul/utils/localization.dart'

localization(context).appName;

Assets

The following related things are called Assets.

  • Images
  • Icons
  • Fonts

Things related to assets are created under the res folder.

└── res
    ├── icons
    │   └── logo.png
    ├── images
    │   └── logo.png
    └── fonts // <- If you want, you have to make it here yourself.

Organizing assets

To use the image or icon, you can use it as below.

import 'package:flutter_seoul/utils/asset.dart' as asset;
...

Image(
  image: asset.Images.logo,
)

Environment variables

This project has a flutter_dotenv installed. Enter the environmental variable you want in the .env file as follows.

FOO=foo
BAR=bar
FOOBAR=$FOO$BAR
ESCAPED_DOLLAR_SIGN='$1000'
# This is a comment

Using environment variables

import 'package:flutter_dotenv/flutter_dotenv.dart';

dotenv.get('FOO');

Logging

This project has a logger installed

Using the logging

logger.d('Log message with 2 methods');
logger.i('Info message');
logger.w('Just a warning!');
logger.e('Error! Something bad happened');

logger

Native splash screen

This project has a flutter_native_splash package installed. This makes it possible to show the native splash screen. Please refer to this link for instructions on how to use it.

native_splash_image

Freezed

Lastly, freezed package is used in the boilerplate.

After creating the model, you need to run the below command to generate and sync the data structure.

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

If using vscode, you can download extension for auto build.

Simple usage

In current boilerplate, user.dart file is created and below code is written.

import "package:freezed_annotation/freezed_annotation.dart";

part "user.freezed.dart";
part "user.g.dart";

@freezed
class User with _$User {
  const factory User({
    required String displayName,
    required int age,
    required String organization,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

Running flutter pub run build_runner build --delete-conflicting-outputs, will generate b60b3d5.