Боевой опыт Flutter фермера переднего плана

Flutter

предисловие

Когда React Native был в огне, я ударил одного из нихместоКлиент , недавно нашел время написать свой собственный проект на Flutter, адрес проектакликните сюда, я хотел бы дать вам звезду🌟, когда вы будете проходить мимо, большое спасибо; Ниже приводится краткое изложение некоторых взглядов и опыта использования Flutter в качестве внешнего интерфейса;


Dart

Когда я начал писать Flutter, я сначала не изучал Dart. Я думал, что он немного похож на TypeScript. С Dart было легко начать работу. Я прочитал его только тогда, когда столкнулся с некоторыми незнакомыми проблемами.Дарт Документация, поговорим о некоторых разных понятиях:

  • объявление переменной

    1. var

      Как в JavaScript, так и в Dart он может принимать любой тип, но как только переменная var в Dart будет назначена, тип будет определен, и его тип нельзя будет изменить;

      var a;
      a = 'hello'; // a 已经确定为String类型
      a = 1; // 报错,类型不能更改
      
    2. dynamic & Object

      В javaScript нет объявления динамической переменной, в отличие от var, обе они поддерживают изменение типа переменной после объявления, но переменная, объявленная Object, может использовать только свойства и методы, принадлежащие Object, а dynamic поддерживает все свойства.

    3. final & const

      Буквально обе объявлены константами, но переменные const являются константами времени компиляции, а переменные final инициализируются при первом использовании;

  • Асинхронная поддержка

    И async, и await одинаково используются в Javascript и Dart, но обещания нет, вместо этого есть Future, но нет разрешения и отказа.

  • Конструктор В Dart подклассы не наследуют именованный конструктор суперкласса. Если вы явно не предоставляете конструктор подкласса, система предоставляет конструктор по умолчанию. При этом способ написания стал более лаконичным;

        class Point {
          num x;
          num y;
    
          Point(this.x, this.y);// 这句等同于
          /* 
          Point(num x, num y) {
            this.x = x;
            this.y = y;
          }
          */
        }
    
  • стрелочная функция

    В Javascript функция стрелки существует как область действия, которая влияет на это, но в Dart она существует как сокращенный синтаксис.Концепции этих двух различны и должны быть четко различаются;


Макет пользовательского интерфейса

Во-первых, давайте посмотрим на тот же макет, используя HTML + CSS и разницу в написании Flutter.

Во Flutter весь UI основан на виджете, на картинке выше Container — это виджет, который стилизован по стилю (можно также использовать Theme, о чем будет подробно рассказано позже), а подклассы вложены в дочерние.

class MainApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomePage(),
    );
  }
}

По сути, этот способ написания немного похож на виртуальный Дом, который написан в древовидной вложенности, но такой способ написания лично мне кажется, что его очень сложно поддерживать.Если не будет хватать подразделенных компонентов, читабельность тоже станет очень плохо На самом деле, во Flutter также есть проблемы сОбсуждение JSX-подобного письма, Чтобы пожаловаться на такую ​​манеру написания, я недавно видел очень подходящую картинку по температуре кипения наггетсов:

О виджете см.Каталог виджетов китайской сети Flutter, не буду писать подробности, вот несколько нетипичных моментов, требующих внимания:

  1. ExpandedНельзя использовать для виджетов неопределенной или бесконечной высоты (таких какSingleChildScrollView) середина

  2. BuildContextКонцепция чего-либо

    BuildContextПо сути, это объект Element, созданный текущим виджетом.Чтобы получить размер компонента, нужно использоватьMediaQuery.of(context).size, также используется при маршрутизации переходовNavigator.of(context), более подробные инструкции по расширению и пониманию можно найти вГлубокое понимание BuildContextЭта статья;

  3. Управление состоянием виджета

    Здесь, чтобы представитьInheritedWidget,InheritedWidget— это специальный виджет, который можно поместить в дерево виджетов в качестве родителя другого поддерева. Все дочерние виджеты этого поддерева могут взаимодействовать с этимInheritedWidgetРаскрываемые данные взаимодействуют, тем самым реализуя связь между WIDGET; можно указать дополнительные способы управления состоянием.Глубокое погружение в управление состоянием во флаттере

стиль

Во Flutter стили не извлекаются, а используются в различных (хаотичных и даже странных) комбинациях.TextStyle, установить фон границы и т. д. для использованияdecoration, можете посмотреть, если интересноНекоторые сравнения использования стилей;

Здесь я хочу плюнуть на управление стилями, во Flutter можно использоватьThemeдля совместного использования стилей, но стили одного виджета являются дополнением кDefaultTextStyleНет никакого наследования кроме установки стиля текста по умолчанию.Все равно их надо писать по одному.Здесь мы продвигаем доработку компонентов(иначе будет лень писать повторно).Тема имеет следующие способы использования

  • глобальная тема

    new MaterialApp(
      title: title,
      theme: new ThemeData(
          brightness: Brightness.dark,
      ),
    );
    
  • местная тема

    new Theme(
      data: new ThemeData(
          accentColor: Colors.yellow,
      ),
      child: new Text('Hello World'),
    );
    
  • Развернуть тему

    Если вы не хотите переопределять все стили, вы можете унаследовать тему приложения и переопределить только некоторые стили, используя метод copyWith.

    new Theme(
      data: Theme.of(context).copyWith(accentColor: Colors.yellow),
      child: new Text('extend theme'),
    );
    
  • получить тему

    Theme.of(context)Будет искать дерево виджетов и возвращать ближайший объект Theme. Если на родительском уровне есть объект Тема, возвращается эта Тема, если нет, возвращается Тема Приложения. Чтобы создать тему, просто вызовите ее через метод Theme.of(context) в конструкторе виджета.

    new Container(
      color: Theme.of(context).accentColor,
      chile: new Text(
          'Text with a background color',
          style: Theme.of(context).textTheme.title,
      ),
    );
    

государственная составляющая

С сохранением состояния и без состояния

Любой, кто использовал React, знает компоненты без состояния и компоненты с состоянием.StatelessWidgetЭто компонент без состояния, который не зависит ни от каких других данных, кроме переданных данных, а это означает, что изменение параметров, переданных в его конструктор, — единственный способ изменить его отображение. иStatefulWidgetЭто компонент с состоянием, но он немного отличается от React.renderи состояние вместе, и во Флаттере,StatefulWidgetнужно переписатьcreateStae(), возвращает состояние иbuildМетод нужно поместить в State, а почему бы и не в StatefulWidget? Есть две причины:

  1. проблема с доступом к состоянию

    так какbuildМетод вызывается каждый раз при изменении состояния, вStatefulWidgetКогда много состояний,buildМетод должен передавать параметр State.Тогда все состояния State могут быть доступны только за пределами класса State, но после того, как состояние будет раскрыто, состояние больше не будет частным, поэтому изменение состояния станет неуправляемый;

    Widget build(BuildContext context, State state){
      //state.a etc...
      ...
     }
    
  2. Проблема наследования StatefulWidget

    Когда возникает первая ситуация, если есть дочерний виджет, который наследуется от абстрактного метода, который вводитbuild(BuildContext context)Родительский виджет, затем дочерний виджет реализует этоbuildВ это время родительский виджет должен передать свое состояние дочернему виджету, что очень неразумно, поскольку состояние родительского виджета связано только с его собственной логикой, и его нужно передать дочернему виджету. , следовательно, должно бытьbuildМетоды помещаются в состояние.

      class ChildWidgert extends ParentWidget{
         @override
         Widget build(BuildContext context, State state){
          super.build(context, _parentWidgetState)
         }
      }
    

жизненный цикл

Жизненный цикл Flutter выглядит следующим образом:

Скажите некоторые часто используемые:

  1. initState

    Эта функция эквивалентна инициализации State в конструкторе в React, и на этом шаге можно выполнить загрузку запроса данных.

  2. didUpdateWidget

    когда звонятsetStateПри изменении состояния виджета Flutter создаст новый виджет для привязки этого состояния и передаст старый виджет в этом методе, если вы хотите сравнить старый и новый виджет и внести некоторые коррективы в состояние, или некоторые виджеты включают изменения в состояние. controller , вы можете удалить старый контроллер и создать новый контроллер в этом методе обратного вызова;

    @override
    void didUpdateWidget(AVCycleLess oldWidget){
      super.didUpdateWidget(oldWidget);
    }
    
  3. dispose

    Когда виджет освобождается (например, при переключении маршрута), в виджете есть некоторые отслеживаемые или постоянные переменные, и вам необходимо освободить его.

FutureBuilder

Когда мы заходим на страницу для выполнения некоторых трудоемких операций, таких как запрос данных, инициализация некоторых настроек и т. д., нам обычно нужно отобразить страницу загрузки.Общая практика заключается в оценке состояния данных для переключения отображаемых компонентов и во флаттере естьFutureBuilderЭто удобное решение будет здесь очень длинным, вы можете обратиться кКак использовать FutureBuilder и вопросы, требующие внимания

маршрутизация

Во Flutter маршрутизация делится на статическую маршрутизацию и динамическую маршрутизацию.Статическая маршрутизация не может передавать параметры, поэтому, когда необходимо передать параметры, можно использовать только динамическую маршрутизацию;

статическая маршрутизация

Статические маршруты определяются при создании нового приложения, используйтеNavigator.of(context).pushNamed('/router/a');Для переключения pushNamed возвращает Future, который может получить возвращаемое значение со следующей страницы.

return new MaterialApp(
    home: new Text('hello'),
    routes: <String, WidgetBuilder> {
        '/router/a': (_) => new APage(),
        '/router/b': (_) => new BPage(),
    },
);
// then 说明
// 当前页面
Navigator.of(context).pushNamed('/router/b').then((value) {
    // value 为下一个页面的返回值
});
// b 页面
Navigator.of(context).pop('some data');

динамическая маршрутизация

Используйте динамическую маршрутизациюpushметод, передать объект маршрута, создать новый объект страницы в конструкторе, если вам нужно настроить эффект анимации, просто используйтеPageRouteBuilderзаменятьMaterialPageRoute,существуетtransitionsBuilderВы можете определить анимацию в .

Navigator.of(context).push(new MaterialPageRoute(builder: (_) {
    return new NewPage(data: 'some data');
}));

сетевой запрос

Dio

Во Flutter сетевые запросы выполняютсяHttpClientОднако операция очень хлопотная, поэтомуDioТакая отличная библиотека запросов упрощает нашу работу, следует отметить, что когда приложение имеет только один источник данных, Dio должен использовать шаблон singleton.

Сериализация

Когда мы получаем данные, мы обычно получаем JSON, В JavaScript мы можем напрямую использовать оператор точки, чтобы получить поля в данных, но в Dart вам нужно импортироватьdart:convertи использоватьJSON.decode(json), но возвращаетMap<String, dynamic>, означает, что мы не знаем тип значения до времени выполнения, и мы теряем большинство функций статически типизированного языка: безопасность типов, автодополнение и, что наиболее важно, исключения времени компиляции.

Но тогда наш код может стать очень подверженным ошибкам. Обычно нам нужно писать классы моделей для сериализации JSON, что официально рекомендуется.json_serializable(См. соответствующие операцииздесь), чтобы помочь нам сгенерировать сериализацию библиотеки JSON, таким образом, мы можем напрямую использовать оператор точки для управления данными.

Если это все еще проблематично, вы можете попробоватьJSONFormat4FlutterЭтот инструмент (я еще не использовал его, он выглядит очень хорошо.)


обработка событий

В Vue нам просто нужно использовать@clickВы можете прослушивать события, используя такие методы, какonClickнапример, но во Flutter нам нужно обернуть элементы, которые должны прослушивать события, вGestureDetectorв использованииonTapи другие методы для обработки событий, а для поведения событий мы можем установитьbehaviorконтролировать,

enum HitTestBehavior {
  deferToChild, // 子widget会一个接一个的进行命中测试,如果子Widget中有测试通过的,则当前Widget通过,这就意味着,如果指针事件作用于子Widget上时,其父(祖先)Widget也肯定可以收到该事件。
  opaque,// 在命中测试时,将当前Widget当成不透明处理(即使本身是透明的),最终的效果相当于当前Widget的整个区域都是点击区域
  translucent,// 当点击Widget透明区域时,可以对自身边界内及底部可视区域都进行命中测试,这意味着点击顶部widget透明区域时,顶部widget和底部widget都可以接收到事件
}

Canvas

Во Flutter, если нам нужно использовать Canvas, нам нужно наследовать CustomPainter и переопределить метод рисования для рисования пользовательской графики. При использовании Canvas нам нужно знать три понятия:

  • canvas

    Объект Canvas, включая различные методы рисования, используемые для рисования различной графики.

  • size

    размер текущей области рисования

  • paint

    Кисти, используемые для управления различными свойствами рисунка, такими как цвет, обводка и сглаживание;

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

class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
      canvas.drawRect(Offset.zero & size, Paint()
      ..isAntiAlias = true // 抗锯齿
      ..style = PaintingStyle.fill // 填充,stroke则为使用描边
      ..color = Color(0xFF000000) // yanse
      );
  }
  @override
  bool shouldRepaint(CustomPainter oldDelegate) => false; // 强制不重绘,提高性能
}

мультиплекс

Mixin

Когда дело доходит до миксинов, я полагаю, что пользователи Vue и React знакомы с ними.Хотя миксины были заменены функциями более высокого порядка или декораторами в React, во Flutter миксины все еще сохраняются. оно используетwithЧтобы ввести миксин, определение выглядит следующим образом:

class A {
  int a = 1;
  void b(){
    print('c');
  }
}

class B with A{

}
B b = new B();
print(b.a);
b.b();

Однако примеси имеют следующие условия для использования в Dart:

  • классы миксинов могут наследоваться только от объекта
  • классы миксинов не могут иметь конструкторов
  • Класс может примешивать несколько классов примесей
  • Может смешивать несколько классов, не нарушая единого наследования Flutter

Keep-alive

При использовании вкладки, после переключения вкладки, каждая вкладка будет уничтожена и перестроена, поэтому initState будет вызываться несколько раз.Есть ли что-то подобное в Vue?<keep-alive>А как насчет тех же компонентов? Ответ да, то естьAutomaticKeepAliveClientMixin. Просто унаследуйте этот миксин и реализуйтеwantKeepAliveметод. Однако виджет не будет уничтожен после того, как он не будет отображаться, а все еще хранится в памяти, поэтому используйте этот метод с осторожностью.

class APageState extends State<APage> with AutomaticKeepAliveClientMixin {
  @override
  bool get wantKeepAlive => true;
  // ...
}

позже

Вышеизложенное — это только то, что я узнал, когда делал первое грубое приложение Flutter с перерывами за последние 10 дней, Некоторые из них — это некоторые знания, которые я увидел в процессе проверки данных, которые не использовались в проекте, и там много подробных или неизведанных вещей. То, в чем я был, стоит обсудить, и я расскажу об этом позже, когда у меня будет возможность.


Ссылаться на