Flutter: уведомления

Для Flutter существуют несколько пакетов для отображения уведомлений. Я использую flutter_easyloading, т.к. в нём по мимо собственно различного вида уведомлений, добавлена и анимация прогресбара. Который можно например использовать при загрузке сетевых запросов.

Установка в pubspec.yaml:

dependencies:
  flutter_easyloading: ^3.0.5

Пример использования:

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        builder: EasyLoading.init(),
....
                        onPressed: (){
                          EasyLoading.show(status: 'loading...');
                          http.post(Uri.parse('https://кацукацуhp'),body: {'login':_controller1.text,'password':_controller2.text}).then((response) {
....
EasyLoading.dismiss();

Есть и некая кастомизация:

void configLoading() {
  EasyLoading.instance
    ..displayDuration = const Duration(milliseconds: 2000)
    ..indicatorType = EasyLoadingIndicatorType.fadingCircle
    ..loadingStyle = EasyLoadingStyle.dark
    ..indicatorSize = 45.0
    ..radius = 10.0
    ..progressColor = Colors.yellow
    ..backgroundColor = Colors.green
    ..indicatorColor = Colors.yellow
    ..textColor = Colors.yellow
    ..maskColor = Colors.blue.withOpacity(0.5)
    ..userInteractions = true
    ..dismissOnTap = false
    ..customAnimation = CustomAnimation();
}

Flutter: работа с WebSocket

Получение данных по протоколу http/http как уже описывал это конечно хорошо, НО! обмен «онлайн» гораздо интереснее. Чтобы сервер в любой момент мог отправить информацию в приложение. И есть решение: при запуске приложения устанавливать соединен с сервером Websocket и дальше уже обменивать информацией по этому протоколу.

Пакет:

dependencies:
  web_socket_channel: ^2.2.0

Пример кода:

import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/status.dart' as status;
import 'dart:convert';
import 'dart:core';

void main() async {

  final channel = await IOWebSocketChannel.connect('ws://укау.цукац.ru:45500',pingInterval: Duration(seconds: 1));

  channel.stream.listen((message) {
    Map<String, dynamic> msg = jsonDecode(message);
     if (msg["command"]=="who_are_you"){
       channel.sink.add('{"command":"who_am_i","user":"10","type":"user"}');
     };
  });

...

Flutter: полноэкранный режим

В ранних версиях Flutter (до 3), полноэкранный режим приложения можно было выставить при помощи пакета Utils. Теперь же одним из вариантов может быть или пакет window_manager:

 WidgetsFlutterBinding.ensureInitialized();
  // Must add this line.
  await windowManager.ensureInitialized();

  // Use it only after calling hiddenWindowAtLaunch
  windowManager.waitUntilReadyToShow().then((_) async{
    // Hide window title bar
    await windowManager.setFullscreen(true);
  });

Или при помощи Flamer:

WidgetsFlutterBinding.ensureInitialized();
  await Flame.device.fullScreen();
  await Flame.device.setLandscape();

Однако ни тот ни другой способ не работают под Windows. Посему вопрос остается открытым..

Flutter: виджет Card

Данный виджет предназначен для того чтобы отобразить что-то в отдельной области «с тенью».

Например:

import 'package:flutter/material.dart';
import 'package:invent/globals.dart' as globals;

class TMCView extends StatefulWidget {
  _TMCViewState createState() => _TMCViewState(); // сюда передаем текущее состояние страницы
}
class _TMCViewState extends State<TMCView> {// _ впереди класса, означает чтоб скрыть доступ из другх файлов
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Найденое ТМЦ')),
      body: Center(
          child: Card (
            margin: EdgeInsets.all(10),
            color: Colors.green[100],
            shadowColor: Colors.blueGrey,
            elevation: 20,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                ListTile(
                  leading: Image.network(
                    'https://picsum.photos/seed/49/600',
                    width: 100,
                    height: 100,
                    fit: BoxFit.cover,
                  ),
                  title: Text(
                    "Принтер",
                    style: TextStyle(fontSize: 20),
                  ),
                  subtitle: Text('Хороший принтер, нужно брать..'),
                ),
                ButtonBar(
                    mainAxisSize: MainAxisSize.min,
                    children: <Widget>[
                      ElevatedButton(
                        child: new Text('Добавить в найденые'),
                        onPressed: (){
                          Navigator.pushNamed(context, "/");
                        },
                      ),
                    ]

                ),
              ],
            ),
          )
      ),
    );
  }
}

Получим:

Flutter: виджет динамического списка

Теперь, когда мы умеем читать данные из сети, сохранять их локально, осталось научится отображать их в виде красивого списка на отдельном экране. Создадим отдельный файл tmclist.dart:

import 'package:flutter/material.dart';
import 'package:invent/globals.dart' as globals;

class TMCList extends StatefulWidget {
  _TMCListState createState() => _TMCListState(); // сюда передаем текущее состояние страницы
}
class _TMCListState extends State<TMCList> {// _ впереди класса, означает чтоб скрыть доступ из другх файлов
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Список ТМЦ')),
      body: Center()
}
      )
    );
  }
}

И далее в body вставляем виджет ListView.builder который может строить список на основе списка данных. В простейшем случае это было бы:

body: ListView.builder(
            padding: const EdgeInsets.all(8),
            itemCount: users.length,
            itemBuilder: (BuildContext context, int index) {
              return Container(
                padding: EdgeInsets.symmetric(vertical: 10),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(globals.tmclist[index]["name"],, style: TextStyle(fontSize: 22)),
                  ],
                )
              );
            }
        ),

Но так не интересно, просто список без «украшений», потому я чкть усложнил, но суть остается едина:

 body: ListView.builder(
        padding: const EdgeInsets.all(8),
        itemCount: globals.tmclist.length,
        itemBuilder: (BuildContext context, int index) {
          return Container(
            padding: EdgeInsets.symmetric(vertical: 10),
            child: Column(
              mainAxisSize: MainAxisSize.max,
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                Align(
                  alignment: AlignmentDirectional(0, 0),
                  child: Row(
                    mainAxisSize: MainAxisSize.max,
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Align(
                        alignment: AlignmentDirectional(0.05, 0),
                        child: Text(
                          globals.tmclist[index]["id"],
                          textAlign: TextAlign.start,
                        ),
                      ),
                      Text(
                        globals.tmclist[index]["name"],
                        textAlign: TextAlign.start,
                        style: TextStyle(fontSize: 22)
                      ),
                      Image.network(
                        'https://picsum.photos/seed/49/600',
                        width: 100,
                        height: 100,
                        fit: BoxFit.cover,
                      ),
                      new Divider()
                    ],
                  ),
                ),
                Text(
                    globals.tmclist[index]["comment"],
                    style: TextStyle(fontSize: 18)
                ),
              ],
            ),
          );
        }
      )

Результат:

Остался вопрос, как навесить действие на выбор какого то пункта, и распознать какой собственно пункт выбрали? А вот для этого служит специальный виджет GestureDetector, который собственно это всё и позволяет делать:

  appBar: AppBar(title: Text('Список документов инвентаризации')),
        body: ListView.builder(
            padding: const EdgeInsets.all(8),
            itemCount: globals.MyInvents.length,
            itemBuilder: (BuildContext context, int index) {
              return
                GestureDetector(
                      onTap: (){
                        print(index);
                      },
                      child: Container(
                      padding: EdgeInsets.symmetric(vertical: 10),
                      child: Column(
                        mainAxisSize: MainAxisSize.max,
                        mainAxisAlignment: MainAxisAlignment.spaceAround,
                        children: [
...

Получим при клике по пунктам меню:

1 4 5 6 7 8 10