Flutter: белый экран во время запуска приложения

У приложений под Android есть такое неприятное свойство, что инициализация первого экрана может продлится довольно большое время. И всё это время телефон будет показывать не красивый белый экран. Однако решение есть, причём совершенно стандартное. Уже заранее всё сделано, осталось только добавить картинку в /android/app/src/main/res/drawable (если сборка под API <21) или /android/app/src/main/res/drawable-v21, и поправить соответствующий файл /android/app/src/main/res/drawable-v21/launch_background.xml приведя его например к виду:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@android:color/background_dark" />
     <item>
        <bitmap
            android:scaleType="fitCenter"
            android:src="@mipmap/loading" />
    </item>
</layer-list>

В итоге при запуске приложения экран будет более приятным:

Flutter: checkbox в приложении

Для того чтобы получить отображение чекбокса вида:

Необходимо использовать виджет Checkbox. Положим его в контейнер, а контейнер разделим на две части: в первой будет текст, а во второй чекбокс:

                      return Container(
                        padding: EdgeInsets.symmetric(vertical: 1),
                        child:
                          Row(
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children:<Widget>[
                                Expanded(
                                  flex: 2, // 60%
                                  child: Column(
                                    crossAxisAlignment: CrossAxisAlignment.start,
                                    children: [
                                      Align(
                                        alignment: AlignmentDirectional(-1, 0),
                                        child:
                                        Text("Какой то чекбокс", style: TextStyle(fontSize: 22)),
                                      ),
                                    ],
                                  )
                                ),
                                Expanded(
                                flex: 2, // 60%
                                child:
                                  Checkbox(
                                      value: true,
                                      onChanged: (bool? value) { // здесь мы можем обновить какую то переменную после выбора
                                        setState(() {
                                          
                                        });
                                      }
                                  )
                                )
                              ]
                          )
                        );

Табы в приложении

Одним из вариантов организации меню в приложении, является организация «табов», которые могут располагаться как внизу экрана, так и вверху. Например что-то вроде:

Это изображение имеет пустой атрибут alt; его имя файла - %D0%B8%D0%B7%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B5%D0%BD%D0%B8%D0%B5-2.png

Для начала создадим отдельную функцию отрисовки табов:

class _MainPageState extends State<MainPage> {// _ впереди класса, означает чтоб скрыть доступ из другх файлов
  final scaffoldKey = GlobalKey<ScaffoldState>();

  TabBar createTabBar()  {
    return TabBar(
      tabs: [
        Row (children: [Icon(Icons.ac_unit), SizedBox(width:5), Text("Датчики")]),
        Row (children: [Icon(Icons.adjust), SizedBox(width:5), Text("Реле")]),
        Row (children: [Icon(Icons.add_to_queue), SizedBox(width:5), Text("Свет")]),
        Row (children: [Icon(Icons.accessibility_sharp), SizedBox(width:5), Text("Комфорт")]),
      ],
      isScrollable: true,
    );
  }

isScrollable — отвечает за плавное переключение табов. Если эффект анимации не нужен — можно отключить.

Далее главный виджет:

@override
  Widget build(BuildContext context) {
    EdgeInsets a2; EdgeInsetsDirectional a;
    return DefaultTabController(
      length: 4, // количество табов
      child:Scaffold(
          appBar: AppBar(
            bottom: createTabBar(), // функция - построитель табов
            title: Text("Усадьба Мальгино"),
          ),
          body: TabBarView(
              children: [
                Text("1"),
                Text("2"),
                Text("3"),
                Text("4"),
          ]
          )
      )
    );

При отрисовке в методе TabBar можно использовать следующие настройки:

  • isScrollable = true/false = плавный скроллинг
  • padding = отступы
  • indicatorColor = цвет подсветки
  • indicatorWeight = 2.0 = толщина подчёркивания
  • indicatorPadding = EdgeInsets.zero = отступы подчёркивания
  • indicatorSize = размер чёрточки
  • labelColor = цвет текста таба
  • labelStyle = стиль текста таба
  • labelPadding = отступы между метками
  • unselectedLabelColor = цвет не выделенного таба
  • unselectedLabelStyle = стиль не выделенного таба
  • onTap = действие при выборе

Flutter: реализация «смахивания» в приложении.

Задача: реализовать удаление позиции из списка «смахиванием».

Решение: используем для этого виджет Dismissible. Обернем в него каждый пункт в ListView. Ну собственно в него можно оборачивать любой виджет.

  child: ListView.builder(
....
...
                                child:
                                Dismissible(
                                    key: UniqueKey(),
                                    direction:DismissDirection.startToEnd,
                                    onDismissed: (DismissDirection direction){
                                        TDialogs dia = TDialogs();
                                        dia.SureDialog(context, "Подтверждение", "Вы действительно удалить ТМЦ  "+globals.TMCList[index]["inv_num"]+" ?",
                                          (){
                                            setState(() {
                                                                                            globals.TMCList.removeAt(index);
                                            });
                                          },
                                          (){
                                            setState(() {});
                                          }
                                        );
                                    },
                                    child: Container(
                                        padding: EdgeInsets.symmetric(vertical: 10),
                                        color: globals.TMCList[index]["count"]==0?Colors.red:null,
                                        child: Column(
                                          children: [
                                            Text(
                                              globals.TMCList[index]["name"],
                                              textAlign: TextAlign.start,
                                            ),
                                            Align(

....

Из интересного: метод direction отвечает за то, как именно разрешено «смахивать». В примере это «от старта до конца». Т.е. слева на право. Доступные варианты:

enum DismissDirection {
  vertical,
  horizontal,
  endToStart,
  startToEnd,
  up,
  down,
  none
}

Flutter: скроллинг до элемента в списке

Задача: позиционировать по нажатию кнопки список на нужном элементе списка.

Решение: используем пакет scroll_to_index:

dependencies:
  scroll_to_index : any

Код:

class _MainMenuState extends State<MainMenu> {
  final scaffoldKey = GlobalKey<ScaffoldState>();

  final scrollDirection = Axis.vertical;
  late AutoScrollController controller;

  @override
  void initState() {
    print("-инициализация класса..");
    controller = AutoScrollController(
        viewportBoundaryGetter: () =>
            Rect.fromLTRB(0, 0, 0, MediaQuery.of(context).padding.bottom),
            suggestedRowHeight: 2000, // если большой список, и  скролл визуально идёт медленно, то это "шаг" скролла. Т.е. чем больше, тем быстрее.
            axis: scrollDirection
    );
  }

  Future _scrollToCounter(int counter) async {

    await controller.scrollToIndex(counter, duration: Duration(seconds: 1));
    controller.highlight(counter);
  }
....
...
      // список ТМЦ для инвентаризации (если есть)
      body: ListView.builder(
          physics: NeverScrollableScrollPhysics(),
          scrollDirection: scrollDirection,
          controller: controller,
          padding: const EdgeInsets.all(8),
          itemCount: globals.TMCList.length,
          itemBuilder: (BuildContext context, int index) {
            return AutoScrollTag(
              key: ValueKey(index),
              controller: controller,
              index: index,
              child: Container(
                  padding: EdgeInsets.symmetric(vertical: 10),
                  child: Column(
                    children: [
                      Text(
                        globals.TMCList[index]["name"],
                        textAlign: TextAlign.start,
                      ),
                      Align(
.....
        floatingActionButton: FloatingActionButton(
          onPressed: (){
_scrollToCounter(10);
          },
1 2 3 4 5 6 9