Учебное пособие по Flutter для фронтенд-инженеров

внешний интерфейс

Добавить Автора

| Введение Наиболее полное руководство по Flutter, включающее язык Dart, виджет Flutter, управление состоянием Flutter и т. д.

Самый метательный — это фронтенд-инженер, с jQuery на AngularJs, а потом на Vue, React. Тот, кто любит кросс-скрин, также является фронтенд-инженером, от phonegap до React Native, а затем и до Flutter.

Представьте что?

бюджетныйдовести до пользователейУлучшенный пользовательский опыт.

В настоящее времяFlutterПожалуй, лучший из всех.

Что такое флаттер?

Flutter — это инструментарий пользовательского интерфейса Google для создания красивых, скомпилированных в собственном коде приложений дляmobile, web, and desktopfrom a single codebase.

FlutterОн разработан на основе первоначальных членов команды Google Chrome, использующих механизм 2D-рендеринга Chrome, а затем упрощающий макет CSS.

img

[Архитектура флаттера]

или более подробный вариант:

img

  • На каждой нативной платформе Flutter использует свой собственный движок C++ для визуализации интерфейса, не использует веб-просмотр и не использует системные компоненты, такие как RN и NativeScript. Проще говоря, платформа просто предоставляет Flutter холст.
  • Интерфейс разработан на языке Dart, который, похоже, является единственным строго типизированным языком, поддерживающим режимы JIT и AOT.
  • Метод написания очень современный, декларативный, компонентный, Композиция > Наследование, отзывчивый... Это тот набор, который сейчас популярен во внешнем интерфейсе :smile:
  • Один набор кода для всех платформ.

Почему флаттер быстрый? Каковы преимущества Flutter перед RN?

Из архитектуры мы действительно можем понять, почему Flutter быстрый, по крайней мере, причину, по которой он быстрее, чем предыдущий популярный жареный цыпленок React Native.

  • Движок Skia, Chrome, Chrome OS, Android, Firefox, Firefox OS используют его в качестве механизма рендеринга.
  • Язык Dart может быть скомпилирован с помощью AOT в код ARM, благодаря чему макет и бизнес-код работают максимально быстро, а сборщик мусора Dart специально оптимизирован для частого уничтожения и создания виджетов с помощью Flutter.
  • Flex-подобный макет, подмножество CSS, сохраняет производительность, сохраняя при этом высокую производительность.
  • Виджеты, написанные бизнесом Flutter, конвертируются в Render Objects перед рендерингом, да, точно так же, как Virtual DOM в React, чтобы обеспечить удобство разработки и производительность.

И по сравнению с React Native:

  • RN использует JavaScript для запуска бизнес-кода, а затем использует JS Bridge для вызова компонентов, связанных с платформой.Соотношение производительности теряется, и даже движки js разные для разных платформ.
  • RN использует компоненты платформы, согласованность поведения будет обесценена, или разработчикам придется иметь дело с дополнительными проблемами, связанными с платформой.

Для конкретного теста производительности двух вы можете увидетьздесь, вывод таков, что Flutter лучше, чем ReactNative по стабильности процессора, FPS и памяти.

Дартс язык

Перед запуском Flutter нам нужно понять язык Dart...

Dart был разработан Google и изначально задумывался как замена языка JavaScript, но после провала и молчания у него есть вторая весна как уникальный язык разработки для Flutter :joy:.

На самом деле, даже в 2.0,Синтаксис дартси JavaScriptFlutterочень похожий. Один поток, цикл событий...

img

[Модель цикла событий дротика]

Конечно, в качестве учебника для фронтенд-инженеров я просто хочу написать что-то, что пока недоступно в JavaScript, и более беззаботно и «приятно» в Dart.

  • не будет плаватьthis

  • Сильная типизация, конечно, во внешнем интерфейсе теперь есть TypeScript :гримасничая:

  • Мощные и удобные рабочие символы:

    • ?.удобно и безопасноfoo?.barзначение, если foonull, то значение равноnull
    • ?? condition?expr1:expr2можно сократить какexpr1??expr2
    • =Комбинации с другими символами:*=,~/=,&=,|=...
    • Каскадная запись..
// 想想这样省了多少变量声明
querySelect('#button')
 ..text ="Confirm"
 ..classes.add('important')
 ..onClick.listen((e) => window.alert('Confirmed'))

Вы даже можете переопределить оператор

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // Operator == and hashCode not shown. For details, see note below.
  // ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}

Примечание:переписать==, также необходимо переопределить объектhashCodegetter

class Person {
  final String firstName, lastName;

  Person(this.firstName, this.lastName);

  // Override hashCode using strategy from Effective Java,
  // Chapter 11.
  @override
  int get hashCode {
    int result = 17;
    result = 37 * result + firstName.hashCode;
    result = 37 * result + lastName.hashCode;
    return result;
  }

  // You should generally implement operator == if you
  // override hashCode.
  @override
  bool operator ==(dynamic other) {
    if (other is! Person) return false;
    Person person = other;
    return (person.firstName == firstName &&
        person.lastName == lastName);
  }
}

void main() {
  var p1 = Person('Bob', 'Smith');
  var p2 = Person('Bob', 'Smith');
  var p3 = 'not a person';
  assert(p1.hashCode == p2.hashCode);
  assert(p1 == p2);
  assert(p1 != p3);
}

Это особенно полезно для объектов diff.

lsolate

Dart работает в изолированном iSolate, как и JavaScript, однопоточном, управляемом событиями, но Dart также открыт для создания других изолятов, чтобы в полной мере использовать возможности ЦП.

loadData() async {
   // 通过spawn新建一个isolate,并绑定静态方法
   ReceivePort receivePort =ReceivePort();
   await Isolate.spawn(dataLoader, receivePort.sendPort);

   // 获取新isolate的监听port
   SendPort sendPort = await receivePort.first;
   // 调用sendReceive自定义方法
   List dataList = await sendReceive(sendPort, 'https://hicc.me/posts');
   print('dataList $dataList');
}

// isolate的绑定方法
static dataLoader(SendPort sendPort) async{
   // 创建监听port,并将sendPort传给外界用来调用
   ReceivePort receivePort =ReceivePort();
   sendPort.send(receivePort.sendPort);

   // 监听外界调用
   await for (var msg in receivePort) {
     String requestURL =msg[0];
     SendPort callbackPort =msg[1];

     Client client = Client();
     Response response = await client.get(requestURL);
     List dataList = json.decode(response.body);
     // 回调返回值给调用者
     callbackPort.send(dataList);
  }    
}

// 创建自己的监听port,并且向新isolate发送消息
Future sendReceive(SendPort sendPort, String url) {
   ReceivePort receivePort =ReceivePort();
   sendPort.send([url, receivePort.sendPort]);
   // 接收到返回值,返回给调用者
   return receivePort.first;
}

Конечно же, он упакован во Flutter.compute, который можно легко использовать, напримерРазбирать большой json в других изолятах.

Dart UI as Code

Значение отдельного предложения здесь заключается в том, что, начиная с React, Flutter и недавнего Apple SwiftUI, декларативное написание компонентов Android Jetpack Compose становится все более и более популярным, а веб-интерфейс использует JSX, чтобы облегчить разработчикам задачу. писать, тогда как Flutter и SwiftUI запускаются напрямую с оптимизации самого языка.

Именованные параметры для классов функций

void test({@required int age,String name}) {
  print(name);
  print(age);
}
// 解决函数调用时候,参数不明确的问题
test(name:"hicc",age: 30)

// 这样对于组件的使用尤为方便
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
  return Scaffold(
      appBar: AppBar(),
      body: Container(),
      floatingActionButton:FloatingActionButton()
    );
  }
}

Большой убийца: коллекция «Если» и коллекция «для»

// collection If
Widget build(BuildContext context) {
  return Row(
    children: [
      IconButton(icon: Icon(Icons.menu)),
      Expanded(child: title),
      if (!isAndroid)
        IconButton(icon: Icon(Icons.search)),
    ],
  );
}
// Collect For
var command = [
  engineDartPath,
  frontendServer,
  for (var root in fileSystemRoots) "--filesystem-root=$root",
  for (var entryPoint in entryPoints)
    if (fileExists("lib/$entryPoint.json")) "lib/$entryPoint",
  mainPath
];

См. больше оптимизаций Dart 2.3 для этогоздесь.

Как написать флаттер

Здесь мы, наконец, подошли к сути.Если вы знакомы с веб-интерфейсом и React, вы будете знакомы с исключениями, которые будут обсуждаться ниже.

img

[ UI=F(state) ]

Все из приложения Flutterlib/main.dartОсновная функция файла начинается с:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}

Метод построения класса Dart возвращает виджет Во Flutter все является виджетом, включая, помимо прочего,

  • Структурные элементы, меню, кнопки и т.д.
  • Элементы класса стиля, шрифт, цвет и т. д.
  • Элементы класса макета, отступы, поля и т. д.
  • навигация
  • жест

Виджет — это специальный класс в Dart, экземпляр которого создается (в Dartновый не является обязательным) вложены друг в друга, ваше приложение представляет собой дерево компонентов (концепция функции ввода Dart,main.dart->main()).

img

[ Flutter Widget Tree ]

Макет виджета

Как упоминалось выше, идея макета Flutter исходит из CSS, а все во Flutter — это виджет, поэтому общий макет также очень прост:

  • Компонент контейнера Контейнер

    • атрибуты украшения украшения, установить цвет фона, фоновое изображение, границу, закругленный угол, тень и градиент и т. д.
    • margin
    • padding
    • alignment
    • width
    • height
  • Прокладка, Центр

  • Row,Column,Flex

  • Обертка, схема потока потока

  • стек, расположение по оси Z

  • ...

Больше можно увидеть здесь

Виджеты во Flutter можно разделить на три категории, такие как «компоненты отображения», «компоненты-контейнеры» и «контекст» в React.

StatelessWidget

Это «экранный компонент» во Flutter, который не сохраняет собственное состояние, а уничтожает и воссоздает заново при изменении внешних параметров. Flutter рекомендует использовать компоненты без состояния, когда это возможно.

StatefulWidget

Компонент состояния похож на «контейнерный компонент» в React, а компонент состояния во Flutter написан немного иначе.

class Counter extends StatefulWidget {
  // This class is the configuration for the state. It holds the
  // values (in this case nothing) provided by the parent and used by the build
  // method of the State. Fields in a Widget subclass are always marked "final".

  @override
  _CounterState createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  int _counter = 0;

  void _increment() {
    setState(() {
      // This call to setState tells the Flutter framework that
      // something has changed in this State, which causes it to rerun
      // the build method below so that the display can reflect the
      // updated values. If you change _counter without calling
      // setState(), then the build method won't be called again,
      // and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance
    // as done by the _increment method above.
    // The Flutter framework has been optimized to make rerunning
    // build methods fast, so that you can just rebuild anything that
    // needs updating rather than having to individually change
    // instances of widgets.
    return Row(
      children: <Widget>[
        RaisedButton(
          onPressed: _increment,
          child: Text('Increment'),
        ),
        Text('Count: $_counter'),
      ],
    );
  }
}

Вы можете видеть, что Flutter напрямую использует то же имя, что и React.setStateметод, но слияния переменных не будет, конечно естьжизненный цикл.

img

[ Жизненный цикл виджета StatefulWidget во флаттере ]

Видно, что компонент с состоянием требует два класса. Причина этого в том, что виджеты во Flutter неизменяемы. Состояние компонента состояния сохраняется в состоянии, и компонент все равно каждый раз пересоздается. Виджет — это только своего рода сопряжение здесь Описание компонента, Flutter преобразует его в элемент, а затем преобразует в RenderObject перед рендерингом.

img

[ Flutter render object ]

Flutter Widget Можно увидеть больше процесса рендерингаздесь.

На самом деле виджет используется только как описание структуры компонента, а также может принести преимущество в том, что можно сделать несколько более удобныхтематические компоненты, официально предоставленный FlutterMaterial Components widgetsиCupertino (iOS-style) widgetsКачество довольно высокое в сочетании с долей секунды FlutterHot Reload, опыт разработки можно сказать, что это неплохо.


State Management

setState()Управлять данными в компоненте очень удобно, но состояние во Flutter также течет сверху вниз, поэтому он столкнется с той же проблемой, что и в React: если дерево компонентов будет слишком глубоким, создать его будет очень хлопотно. состояние слой за слоем., не говоря уже о разборчивости и ремонтопригодности кода.

InheritedWidget

Точно так же Flutter также имеетcontextто же самое, т.InheritedWidget, он также очень прост в использовании.

class GlobalData extends InheritedWidget {
  final int count;
  GlobalData({Key key, this.count,Widget child}):super(key:key,child:child);

  @override
  bool updateShouldNotify(GlobalData oldWidget) {
    return oldWidget.count != count;
  }

  static GlobalData of(BuildContext context) => context.inheritFromWidgetOfExactType(GlobalData);
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: GlobalData(
        count: _counter,
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
              Text(
                '$_counter',
                style: Theme.of(context).textTheme.display1,
              ),
              Body(),
              Body2()
            ],
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

class Body extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    GlobalData globalData = GlobalData.of(context);
    return Text(globalData.count.toString());
  }
}

class Body2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    GlobalData globalData = GlobalData.of(context);
    return Text(globalData.count.toString());
  }

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

BlOC

BlOCЕще одна более продвинутая организация данных, предложенная командой Flutter, и моя любимая. Проще говоря:

Bloc = InheritedWidget + RxDart(Stream)

Steam встроен в язык Dart, Stream ~= Observable, сRxDart, затем добавьтеStreamBuilderЭто был бы исключительно мощный и бесплатный режим.

class GlobalData extends InheritedWidget {
  final int count;
  final Stream<String> timeInterval$ = new Stream.periodic(Duration(seconds: 10)).map((time) => new DateTime.now().toString());
  GlobalData({Key key, this.count,Widget child}):super(key:key,child:child);

  @override
  bool updateShouldNotify(GlobalData oldWidget) {
    return oldWidget.count != count;
  }

  static GlobalData of(BuildContext context) => context.inheritFromWidgetOfExactType(GlobalData);

}

class TimerView extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    GlobalData globalData = GlobalData.of(context);
    return StreamBuilder(
        stream: globalData.timeInterval$,
        builder: (context, snapshot) {
          return Text(snapshot?.data ?? '');
        }
    );
  }
}

Конечно, проблема с Блоком в том, что

  • Стоимость обучения немного выше, и концепция Rx должна быть полностью понята, иначе вы сойдете с ума.
  • Проблема со свободой в том, что код может быть не таким аккуратным, как класс Redux.

Кстати, в этом году Apple тоже взялась за отзывчивость,Combine(Rx вроде) + SwiftUI в основном равен Bloc.

Так что Rx еще надо быстро научиться :гримасничание:

Помимо Bloc, во Flutter можно использовать и другие решения, такие как:

Расширяясь, текущая фронтенд-разработка с использованием мощной сборки страниц фреймворка уже не представляет сложности. Сложность разработки заключается в том, как объединить данные, необходимые для полноценного взаимодействия, как показано на рисунке выше.stateчасть.

Точнее, как элегантно, эффективно и легко обращаться с эфемерным состоянием.setState()А проблема App State, которым нужно делиться, это инженерная проблема, но часто самая сложная в ежедневной разработке.Цитируя автора Redux Дэна:

«Правило большого пальца:Do whatever is less awkward."

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


контрольная работа

Отладчик Flutter, все тесты встроены, пользоваться им несложно.

// 测试在/test/目录下面
void main() {

  testWidgets('Counter increments smoke test', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp());

    // Verify that our counter starts at 0.
    expect(find.text('0'), findsOneWidget);
    expect(find.text('1'), findsNothing);

    // Tap the '+' icon and trigger a frame.
    await tester.tap(find.byIcon(Icons.add));
    await tester.pump();

    // Verify that our counter has incremented.
    expect(find.text('0'), findsNothing);
    expect(find.text('1'), findsOneWidget);
  });
}

управление пакетами, управление ресурсами

Подобно npm в JavaScript, Flutter, то есть Dart, тоже имеет свой собственныйпакетный склад. Однако зависимости пакета проекта описываются с помощью файлов yaml:

name: app
description: A new Flutter project.
version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.2

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

Мобильные приложения всегда требуют жизненных циклов на уровне приложения.Использование хуков жизненного цикла во флаттере также очень просто:

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    switch (state) {
      case AppLifecycleState.inactive:
        print('AppLifecycleState.inactive');
        break;
      case AppLifecycleState.paused:
        print('AppLifecycleState.paused');
        break;
      case AppLifecycleState.resumed:
        print('AppLifecycleState.resumed');
        break;
      case AppLifecycleState.suspending:
        print('AppLifecycleState.suspending');
        break;
    }
    super.didChangeAppLifecycleState(state);
  }

  @override
  Widget build(BuildContext context) {
      return Container();
  }
}

Используйте врожденные способности

Подобно ReactNative, Flutter использует механизм, подобный событию, для использования возможностей конкретной платформы.

img

[ Flutter platform channels ]

Flutter Web, Flutter Desktop

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

img

[Веб-архитектура флаттера]

Помните, что платформа предоставляет только холст для Flutter, и будущее Flutter Desktop может быть очень многообещающим :smile:, вы можете прочитать об этомздесь.

Наконец, каждое решение, каждая технологияПреимущества и недостатки, и даже архитектура технологии определяет, что некоторые дефекты никогда не будут исправлены, так что [эмодзи].


Подпишитесь на официальный аккаунт [IVWEB Community], чтобы получать свежие статьи каждую неделю, ведущие к вершине жизни!