DEV Community

Cover image for How to create a Offline Internationalization App:Use Sqlite database
nasa.wang
nasa.wang

Posted on • Edited on

3 1

How to create a Offline Internationalization App:Use Sqlite database

https://pub.dev/packages/floor

Configure the reference pubspec.yaml of the dependent library in the Flutter project

dependencies:
  flutter:
    sdk: flutter
  floor: ^1.2.0

dev_dependencies:
  floor_generator: ^1.2.0
  build_runner: ^2.1.2
Enter fullscreen mode Exit fullscreen mode

Create entity and view [project_root]/lib/app/data/entity/vegetalbe.dart

import 'package:floor/floor.dart';

@Entity(tableName: "vegetables")
class Vegetable {
  @PrimaryKey(autoGenerate: true)
  final int? id;

  final String name;

  final String locale;

  final String desc;

  @ColumnInfo(name: 'created_at')
  final int createTime;

  @ColumnInfo(name: 'updated_at')
  final int updateTime;

  Vegetable(
    this.id,
    this.name,
    this.locale,
    this.desc, {
    int? createTime,
    int? updateTime,
  })  : this.createTime = createTime ?? DateTime.now().millisecondsSinceEpoch,
        this.updateTime = updateTime ?? DateTime.now().millisecondsSinceEpoch;
}

@DatabaseView(
    'SELECT v.id,   v.name, v.desc, v.locale,   uf.hash,    uf.ext,     v.created_at,   v.updated_at from   vegetables v LEFT OUTER JOIN upload_file_morph ufm on   v.id = ufm.related_id LEFT OUTER JOIN upload_file uf on ufm.upload_file_id = uf.id;',
    viewName: "vegetables_v")
class VegetableV {
  final int id;
  final String name;
  final String locale;
  final String? desc;
  final String? hash;
  final String? ext;

  @ColumnInfo(name: 'created_at')
  final int createTime;

  @ColumnInfo(name: 'updated_at')
  final int updateTime;

  VegetableV(
    this.id,
    this.name,
    this.locale,
    this.desc,
    this.hash,
    this.ext, {
    int? createTime,
    int? updateTime,
  })  : this.createTime = createTime ?? DateTime.now().millisecondsSinceEpoch,
        this.updateTime = updateTime ?? DateTime.now().millisecondsSinceEpoch;
}

Enter fullscreen mode Exit fullscreen mode

For specific details, please refer to https://floor.codes/database-views/

Create "Data Access Objects" according to the view[project_root]/lib/app/data/dao/vegetalbe_dao.dart

import 'package:floor/floor.dart';
import 'package:strapi_flutter_internation_poc/app/data/entity/vegetable.dart';

@dao
abstract class VegetableDao {
  @Query('SELECT * FROM vegetables_v')
  Future<List<VegetableV>> findAll();
}

Enter fullscreen mode Exit fullscreen mode

Create Database management class [project_root]/lib/app/data/database.dart

import 'dart:async';
import 'package:floor/floor.dart';
import 'package:sqflite/sqflite.dart' as sqflite;
// daos
import 'dao/vegetable_dao.dart';
// entitys
import 'entity/vegetable.dart';

part 'database.g.dart'; // the generated code will be there

@Database(version: 1, entities: [Vegetable], views: [VegetableV])
abstract class AppDatabase extends FloorDatabase {
  VegetableDao get vegetableDao;
}
Enter fullscreen mode Exit fullscreen mode

Run Floor's code generator

flutter packages pub run build_runner build
[INFO] Generating build script...
[INFO] Generating build script completed, took 480ms

[INFO] Initializing inputs
[INFO] Reading cached asset graph...
[INFO] Reading cached asset graph completed, took 67ms

[INFO] Checking for updates since last build...
[INFO] Checking for updates since last build completed, took 651ms

[INFO] Running build...
[INFO] 1.1s elapsed, 0/1 actions completed.
[INFO] 2.2s elapsed, 0/1 actions completed.
[INFO] 4.0s elapsed, 0/1 actions completed.
[INFO] 8.4s elapsed, 0/1 actions completed.
[INFO] Running build completed, took 8.8s

[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 34ms

[INFO] Succeeded after 8.8s with 2 outputs (2 actions)
Enter fullscreen mode Exit fullscreen mode

This will generate a database.g.dart in the same directory as database.dart

Use GetX's Service scheme to create db service [project_root]/lib/app/common/services/db_service.dart.dart

Please pay special attention here

Unlike the official documentation of Floor, Floor will generate a sqlite database based on the entity. I will provide the existing database files to Floor for use without generating new database files.

import 'dart:io';
import 'package:get/get.dart';
import 'package:path/path.dart';
import 'package:floor/floor.dart';
import 'package:flutter/services.dart';
import 'package:sqflite/sqflite.dart';
import 'package:strapi_flutter_internation_poc/app/data/database.dart';

class DbService extends GetxService {
  static DbService get to => Get.find();

  late AppDatabase db;

  Future<DbService> init() async {
    final callback = Callback(
      onCreate: (database, version) {},
      onOpen: (database) {
        print('onOpen database');
        getDatabasesPath().then((value) => print(value));
      },
      onUpgrade: (database, startVersion, endVersion) {},
    );

    var dbDir = await getDatabasesPath();
    var dbPath = join(dbDir, "app_database.db");

    await deleteDatabase(dbPath);

    ByteData data = await rootBundle.load("assets/db/data.db");
    List<int> bytes =
        data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
    await File(dbPath).writeAsBytes(bytes);

    db = await $FloorAppDatabase
        .databaseBuilder(dbPath)
        .addCallback(callback)
        .build();
    return this;
  }
}
Enter fullscreen mode Exit fullscreen mode

Instantiate DbService [project_root]/lib/main.dart

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initServices();

  runApp(
    GetMaterialApp(
      title: "Application",
      initialRoute: AppPages.INITIAL,
      getPages: AppPages.routes,
    ),
  );
}

Future<void> initServices() async {
  print('starting services ...');
  await Get.putAsync(() => DbService().init());
  print('All services started...');
}
Enter fullscreen mode Exit fullscreen mode

Modify the home_controller code to read the Sqlite database [project_root]/lib/app/modules/home/controllers/home_controller.dart

import 'package:get/get.dart';
import 'package:strapi_flutter_internation_poc/app/common/services/db_service.dart';
import 'package:strapi_flutter_internation_poc/app/data/entity/vegetable.dart';

class HomeController extends GetxController {
  final vegetables = Rx<List<VegetableV>>([]);

  @override
  void onInit() {
    super.onInit();
  }

  @override
  void onReady() {
    super.onReady();
  }

  Future<void> getAllVegetables() async {
    final result = await DbService.to.db.vegetableDao.findAll();
    vegetables.value = result;
  }

  @override
  void onClose() {}
}

Enter fullscreen mode Exit fullscreen mode

Test it briefly

controller.getAllVegetables();

  Future<void> getAllVegetables() async {
    final result = await DbService.to.db.vegetableDao.findAll();
    vegetables.value = result;
    print(result);
  }
Enter fullscreen mode Exit fullscreen mode

out

I/flutter ( 7396): starting services ...
I/flutter ( 7396): onOpen database
I/flutter ( 7396): /data/user/0/com.nasawz.strapi_flutter_internation_poc.strapi_flutter_internation_poc/databases
I/flutter ( 7396): All services started...
[GETX] Instance "DbService" has been created
[GETX] Instance "DbService" has been initialized
[GETX] Instance "GetMaterialController" has been created
[GETX] Instance "GetMaterialController" has been initialized
[GETX] GOING TO ROUTE /home
[GETX] Instance "HomeController" has been created
[GETX] Instance "HomeController" has been initialized
I/flutter ( 7396): [Instance of 'VegetableV', Instance of 'VegetableV', Instance of 'VegetableV', Instance of 'VegetableV', Instance of 'VegetableV', Instance of 'VegetableV', Instance of 'VegetableV', Instance of 'VegetableV']
Enter fullscreen mode Exit fullscreen mode

success! The data is read out.

Use GetX's Obx feature to display data on the interface

import 'package:flutter/material.dart';

import 'package:get/get.dart';
import '../controllers/home_controller.dart';

class HomeView extends GetView<HomeController> {
  @override
  Widget build(BuildContext context) {
    controller.getAllVegetables();
    return Scaffold(
      appBar: AppBar(
        title: Text('Vegetables'),
        centerTitle: true,
      ),
      body: Obx(() => ListView.builder(
          itemCount: controller.vegetables.value.length,
          itemBuilder: (context, index) {
            var vegetable = controller.vegetables.value[index];
            return Padding(
              padding: const EdgeInsets.all(18.0),
              child: Container(
                child: Row(
                  children: [
                    Container(
                      // color: Colors.red,
                      child: Image.asset(
                        'strapi/public/uploads/thumbnail_${vegetable.hash}${vegetable.ext}',
                        fit: BoxFit.contain,
                        width: 140,
                        height: 140,
                      ),
                    ),
                    Container(
                      width: Get.width - 18 * 2 - 140 - 18,
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            vegetable.name,
                            style: Get.textTheme.headline6,
                          ),
                          Text(
                            vegetable.desc!,
                            style: Get.textTheme.subtitle1,
                            maxLines: 1,
                            overflow: TextOverflow.ellipsis,
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            );
          })),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

image-20211010135908-5m68k62


Image description

Play Store

Apple Store

Sentry blog image

The countdown to March 31 is on.

Make the switch from app center suck less with Sentry.

Read more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay