Flutter: Хранение данных приложения

На этом месте должна была быть статья по работу Flutter с SQLite, но не сложилось. Не умеет Flutter без плясок с бубном работать с SQLite одновременно на всех целевых платформах сразу: WIndows/Android/IOS. Самый популярный пакет sqflite — работает только на Android/IOS, остальные, задекларированные как рабочие под Windows, по оной так запустить мне и не удалось, с учётом что у меня ограниченные права Windows. Потому, стал смотреть в сторону пакета Hive — работающего на всех целевых платформах, т.к. написан изначально на языке Dart.

Hive -это быстрая локальная база для Flutter, Dart работающая на основе ключ-значение.

Установка: добавить в зависимости pubsec.yaml:

dependencies:
  hive: ^2.2.3

Для удобства можно написать маленький класс для работы с ключами-значениями:

import 'dart:io';
import 'package:hive/hive.dart';

class THave {
  String bname="";
  void Init(String basename)  {
    var path = Directory.current.path;
    Hive.init(path);
    bname=basename;
  }
  void Add(String key,var value) async {
    var box = await Hive.openBox(bname);
    await box.put(key, value);
  }
  void Delete(String key) async {
    var box = await Hive.openBox(bname);
    await box.delete(key);
  }
  dynamic  Get(String key) async  {
    var box =  await Hive.openBox(bname);
    return (box.get(key));
  }
  Future <Iterable> GetKeys() async {
    var box = await Hive.openBox(bname);
    return await box.keys;
  }
}

И теперь например можно при новом запуске приложения «вспоминать» авторизацию:

main.dart:

void main() {
  THave have=THave();
  have.Init("auth");
  have.Get("UserId").then((UserId){
    if (UserId!=null){
      globals.UserId=UserId;
    };
    have.Get("UserName").then((UserName){
      if (UserId!=null){
        globals.UserName=UserName;
        globals.is_login=true;
      };
      runApp(const MyApp());
    });
  });
}

login.dart:

                      onPressed: (){
                        http.post(Uri.parse('https://erfwe.ru/1.php'),body: {'login':_controller1.text,'password':_controller2.text}).then((response) {
                          print("Response status: ${response.statusCode}");
                          print("Response body: ${response.body}");
                          Map<String, dynamic> user = jsonDecode(response.body);
                          setState(() {
                            globals.UserName = user["UserName"];
                            globals.is_login = true;
                            globals.UserId = user["UserId"];
                            THave have=THave();
                            have.Init("auth");
                            have.Add("UserName", globals.UserName);
                            have.Add("UserId", globals.UserId);
                          });
                        }).catchError((error){
                          print("Error: $error");
                        });
                      },

Причем нужно отметить свойства сохранения и извлечения ключ-значение: при сохранении мы не ждем результата записи. При чтении — соответственно необходимо дождаться.

Flutter: работа с json

В предыдущей статье мы получили по URL json данные. Теперь задача их обработать.

{
"UserId":10,
"UserName":"Vasya Pukin"
}

Для работы с json, необходимо импортировать библиотеку:

import 'dart:convert';

Далее загрузим данные в тип Map:

 Map<String, dynamic> user = jsonDecode(response.body);
                        print (user["UserName"]);

А что если это массив?

[
  {"UserId":10,"UserName":"Vasya Pukin 1"},
  {"UserId":10,"UserName":"Vasya Pukin 2"}
  ]

В этом случае загрузим JSON в объет List:

                        List users = jsonDecode(response.body);
                        for(int i=0; i < users.length; i++){
                          print(users[i]["UserName"]);
                        }

Обратная конвертация:

                        String json = jsonEncode(user);
                        print (json);

Flutter: сканирование штрихкода

1. Добавим в pubspec.yaml зависимость

dependencies:
  flutter_barcode_scanner: ^2.0.0

Добавим в код предыдущего проекта:

class _Page1State extends State<Page1> { // _ впереди класса, означает чтоб скрыть доступ из других файлов
...
....
  String _scanBarcode = 'Unknown';
  Future<void> startBarcodeScanStream() async {
      FlutterBarcodeScanner.getBarcodeStreamReceiver(
          '#ff6666', 'Cancel', true, ScanMode.BARCODE)!
          .listen((barcode) => print(barcode));
  }
  Future<void> scanBar() async {
    String barcodeScanRes;
    try {
      barcodeScanRes = await FlutterBarcodeScanner.scanBarcode('#ff6666', 'Отменить', false, ScanMode.BARCODE);
      Fluttertoast.showToast(
          msg: "Прочитано:"+barcodeScanRes,
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.CENTER,
          timeInSecForIosWeb: 1,
          backgroundColor: Colors.red,
          textColor: Colors.white,
          fontSize: 16.0
      );
    } on PlatformException {
      barcodeScanRes = 'Ошибка получения версии платформы';
    }
    if (!mounted) return;
    setState(() {
      _scanBarcode = barcodeScanRes;
    });
  }
....
....
....
   ListTile(
                onTap: (){
                  print(scanBar());
                },
                leading: Icon(
                  Icons.sick,
                ),

Следует отметить, что при запуске кода на Android/IOS приложение самостоятельно попросит доступ к камере, а потому еще одной проблемой — обработкой запросов доступа меньше.

Flutter: использование пакетов

Не все йогурты одинаково полезны (с)

Иногда стандартных библиотек поставляемых с Fllutter не хватает, а самому что-то писать затруднительно — выходом может служить использование уже написанного и опакеченого кода. Все пакеты Flutter находятся на pub.dev, для добавления любого из пакетов с этого сайта в проект, достаточно добавить в pubspec.ymal проекта зависимость. Например:

dependencies:
  fluttertoast: ^8.0.9

После чего Android Studio предложит обновить зависимости. Далее добавим в main.dart импортируемую библиотеку:

import 'package:fluttertoast/fluttertoast.dart';

Далее непосредственное использование:

 ListTile(
                onTap: (){
                  Fluttertoast.showToast(
                      msg: "Здраствуйте я ваша дядя..",
                      toastLength: Toast.LENGTH_SHORT,
                      gravity: ToastGravity.CENTER,
                      timeInSecForIosWeb: 1,
                      backgroundColor: Colors.red,
                      textColor: Colors.white,
                      fontSize: 16.0
                  );
                },

Flutter: основные принципы построения приложения

Минимальное приложение Flutter, это:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
debugShowCheckedModeBanner: false, //убираем надпись debug
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),

    );
  }
}

,далее обычно начинают плясать именно от этого шаблона. Запускаем, смотрим…и ничего кроме квадратика с названием окна не видим. Ну собственно ничего более и нет.

Одной из «киллер фич» Flutter считается, что всё что визуально расположено на экране — это виджеты вложенные друг в друга. С «детьми» и «родителями». Принцип разметки примерно такой же как в HTML + CSS: кидаем в «стартовый» виджет другие виджеты различного вида , и добавляем к ним «украшения». В данном случае внутри стартового виджета ничего нет.

Второй «киллер фичей» флаттера является так называемый HotReload — при изменении кода программы, и последующем его изменении, запущеная ранее программа не закрывается, а «обновляется» на лету. Если конечно код не изменен «кардинально»

Добавляем файл-основной экран page1.dart:

import 'package:flutter/material.dart';
class Page1 extends StatefulWidget {
  _Page1State createState() => _Page1State(); // сюда передаем текущее состояние страницы
}
class _Page1State extends State<Page1> { // _ впереди класса, означает чтоб скрыть доступ из другх файлов
  @override
  Widget build(BuildContext context) {
    return Scaffold( // чаще всего используемый "корневой" виджет, который позволяет добавлять в себя другие: AppBar, BottomNavigationBar,Drawer, FloatingActionButton и т.п.
        appBar: AppBar(title: Text("Home page")),
        body: Center(
            child: Text(
              "Ууу как всё запущено пациент..",
              textAlign: TextAlign.center,
              style: Theme.of(context).textTheme.headline3,
            )
        )
    );
  }

}

И далее добавляем в main,dart строчки:

В результате запуска получаем что-то типа:

1 8 9 10 11 12 71