Продолжаем лаконичную интерпретацию официальной документации Flutter в формате «вопрос-ответ». Вот уже 3-я часть, и она в большей степени будет полезна React Native-разработчикам. В данной интерпретации вы можете найти ответы на частые вопросы, а также определить для себя, сколько усилий нужно приложить, чтобы перейти с одного кроссплатформенного фреймворка на другой.
Если этой информации будет недостаточно или у вас есть опыт в нативной разработке под конкретную платформу, то рекомендую заглянуть в другие части:
Flutter. Часть 1. Для Android-разработчиков
Flutter. Часть 2. Для iOS-разработчиков
Flutter. Часть 3. Для разработчиков React Native
Flutter. Часть 4. Для Web-разработчиков
Flutter. Часть 5. Для Xamarin.Forms-разработчиков
Где точка входа?
Функция
В JavaScript нет предопределённой точки входа, ею может быть любая функция, определённая разработчиком. В Dart это только
Как делать вывод в консоль?
С помощью функции
В JavaScript вывод в консоль осуществляется с помощью
Как создавать и присваивать переменные/поля?
Dart поддерживает и динамическую, и строгую типизацию. Поэтому переменные/поля создавать можно в любом удобном вам виде типизации. Присваивание происходит через одинарное =.
В JavaScript строгая типизация не поддерживается.
Подробнее тут.
Какое значение у переменной/поля по умолчанию?
В JavaScript по умолчанию
Подробнее тут.
Как проверять значение на
С помощью явной проверки
В JavaScript в рамках проверки
Как декларировать функции?
В Dart функции, как и поля, могут быть с динамической или строгой типизацией возвращаемого значения. Динамически типизируемые декларируются просто именем функции и опционально параметрами, а строго типизируемые в начале ещё имеют возвращаемый тип.
В JavaScript функция декларируется ключевым словом
Подробнее тут.
Какой аналог у Promise?
Future
Dart, как и JavaScript, поддерживает однопоточное выполнение. Future в Dart по смыслу выполняет те же функции, что и Promise в React Native.
Подробнее тут..
Какой аналог
В JavaScript async-функции возвращают Promise, в Dart — Future. await синхронно ожидает результата вызова async-функции.
Подробнее тут.
Как создать проект приложения на Flutter?
Для создания проекта в React Native используется команда
Подробнее тут.
Как запустить приложение?
Для запуска приложения в React Native используются команды
Как импортировать виджеты?
Во Flutter все виджеты разбиты по пакетам, поэтому достаточно импортировать пакет, чтобы использовать его виджеты.
В React Native необходимо импортировать каждый виджет.
Как использовать виджеты и вложенность для построения дерева виджетов?
Во Flutter почти всё — виджеты. Даже объект приложения — виджет. Каждый виджет может быть вложенным в родительский. Комбинируя виджеты, вы строите иерархию, которая называется «деревом виджетов». Именно она в дальнейшем превращается в отображение на экране.
Как создавать переиспользуемые компоненты?
Во Flutter можно создать класс виджета и переиспользовать его. Используемые параметры являются полями класса.
В React Native для изменения параметров переиспользуемого элемента необходимо их пометить как props.
Параметры в конструкторе могут декларироваться двумя способами: обычным перечислением и опциональным, с помощью фигурных скобок
При обычном перечислении все параметры являются обязательными, и при вызове конструктора нельзя указать, какой конкретно параметр передаётся, т.к. они обусловлены порядком следования.
При опциональном можно использовать любой порядок передачи параметров, но необходимо указать, какой конкретно параметр передаётся или не передавать их вовсе. Чтобы обозначить опциональный параметр как обязательный, нужно использовать аннотацию
Где начинать писать код?
В
Какая структура файлов в проекте Flutter?
Где хранить ресурсы и ассеты и как их использовать?
Ссылки на ресурсы и зависимости проекта хранятся в файле
Подробнее тут.
Декларирование ассетов в
Использование в коде
Как загружать изображения из сети?
С помощью Image.network.
Как подключать сторонние пакеты и плагины?
С помощью
В React Native для добавления зависимостей используются команды
Использование пакетов
Разработка пакетов и плагинов
Популярные плагины для Flutter
Какой аналог у View контейнера?
Все базовые виджеты для вёрcтки, например Container, Column, Row и Center.
Подробнее тут.
Какой аналог FlatList или SelectionList?
ListView
Как использовать Canvas?
С помощью классов CustomPaint и CustomPainter.
В React Native нет возможности рисовать с помощью Canvas из коробки. Есть сторонние плагины, например
Как использовать виджеты для описания свойств вёрстки?
Во Flutter для свойств вёрстки используются специализированные виджеты, например:
Column, Row, Padding, Align, Stack.
В React Native чаще всего свойства вёрстки можно задать с помощью
Полный список виджетов
Как позиционировать виджеты при вёрстке?
Для абсолютного позиционирования используется виджет Stack. Для относительного — различные виджеты и их комбинации, которые можно найти здесь.
Как стилизовать компоненты?
Чаще всего у виджетов есть свойство
В React Native для стилизации используется
Как использовать иконки и цвета?
С помощью классов Icons и Colors.
В React Native нет поддержки иконок из коробки.
Icons
Colors
Как настраивать стилистичекую тему приложения?
С помощью класса ThemeData.
В React Native общая тема настривается через
Пример:
Как обновлять отображение виджетов?
Используя StatefulWidget и его State. Во Flutter есть 2 вида виджетов: StatelessWidget и StatefulWidget. Они работают одинаково, отличие только в состоянии при рендеринге.
StatelessWidget имеет неизменное состояние. Подойдёт для отображения текста, логотипа и т.д. Т.е. если элемент на экране не должен изменяться за всё время отображения, значит, он вам подходит. Его можно использовать и как контейнер для виджетов с изменяемым состоянием.
StatefulWidget имеет состояние State, в котором хранится информация о текущем состоянии. Если вы хотите изменить элемент на экране при выполнении какого-то действия (пришёл ответ с сервера, пользователь нажал на кнопку и т.д.) — это ваш вариант.
1) StatelessWidget — Text
2) StatefulWidget — при нажатии на кнопку (FloatingActionButton) текст в виджете Text меняется с I Like Flutter на Flutter is Awesome!
Как хранить
С помощью плагина shared_preferences.
В React Native используется AsyncStorage.
Подключение зависимости
Использование
Как хранить сложные данные?
С помощью плагинов БД, например, sqflite или hive.
Как навигировать между экранами?
Для навигации между экранами используются классы Navigator и Route.
В React Native используются StackNavigator, TabNavigator и DrawerNavigator.
Во Flutter есть два способа навигации:
Navigator может сделать push() или pop() указанному вами маршруту.
Как использовать tab navigation?
С помощью классов: TabController, TabBar, Tab и TabBarView.
В React Native используются createBottomTabNavigator и TabNavigation.
Подробнее тут..
Как использовать drawer navigation?
С помощью класса Drawer.
В React Native используются createDrawerNavigator и DrawerNavigation.
Как обрабатывать клик?
Если виджет поддерживает клики, то в
В React Native для этого используются PanResponder или Touchable.
Как получать данные из API запросов?
С помощью http-плагина.
В React Native используется
Подключение плагина
Получение данных
Какие виджеты использовать для ввода?
TextField в связке с TextEditingController или TextFormField.
В React Native используется TextInput.
TextFormField отличается от TextField встроенной валидацией и логикой сохранения значений в поля.
TextField
TextFormField
Как определить, на какой платформе выполняется код?
С помощью класса поля
Поле
Класс Platform
Как вызвать нативный платформенный код?
Через MethodChannel.
Подробнее тут..
Какие есть инструменты для отладки приложения?
DevTools.
Как сделать
Если приложение запускалось из IntelliJ IDE или Android Studio, то сочетанием
В React Native для iOS-эмуляторов используется сочетание
Как получить доступ к меню разработчика в приложении?
Если запуск был из IDE, то с помощью инструментов IDE. Если из консоли, то с помощью ввода h.
В React Native используются
Полный список команд
Что используется для анимации?
Animation и AnimationController.
В React Native используется
Подробнее тут.
Как добавить простую
С помощью FadeTransition.
Как добавить анимацию смахивания элементам списка?
С помощью Dismissible.
Какие эквиваленты есть во Flutter по сравнению с React Native?
Как получить доступ к GPS?
С помощью плагина geolocator.
Как получить доступ к камере?
С помощью плагина image_picker.
Как авторизоваться через Facebook?
С помощью плагина flutter_facebook_login.
Как использовать Firebase?
Firebase поддерживает Flutter first party plugins:
Вот, пожалуй, ответы на основные вопросы. Надеюсь, моя интерпретация была для вас полезна, и если вы ещё не пишите на Flutter, то хотя бы задумались об этом. А это значит, что в полку Flutter разработчиков, возможно, будет прибавление, и мы вместе будем делать мир лучше через призму разработанных удобных и быстрых приложений! Да не сломает React ваш Native!
Если этой информации будет недостаточно или у вас есть опыт в нативной разработке под конкретную платформу, то рекомендую заглянуть в другие части:
Flutter. Часть 1. Для Android-разработчиков
Flutter. Часть 2. Для iOS-разработчиков
Flutter. Часть 3. Для разработчиков React Native
Flutter. Часть 4. Для Web-разработчиков
Flutter. Часть 5. Для Xamarin.Forms-разработчиков
Содержание:
- Немного про Dart
- Где точка входа?
- Как делать вывод в консоль?
- Как создавать и присваивать переменные/поля?
- Какое значение у переменной/поля по умолчанию?
- Как проверять значение на null или на 0?
- Как декларировать функции?
- Какой аналог у Promise?
- Какой аналог async и await?
- Основы
- Как создать проект приложения на Flutter?
- Как запустить приложение?
- Как импортировать виджеты?
- Как использовать виджеты и вложенность для построения дерева виджетов?
- Как создавать переиспользуемые компоненты?
- Структура проекта и ресурсы
- Где начинать писать код?
- Какая структура файлов в проекте Flutter?
- Где хранить ресурсы и ассеты и как их использовать?
- Как загружать изображения из сети?
- Как подключать сторонние пакеты и плагины?
- Views
- Какой аналог у View контейнера?
- Какой аналог FlatList или SelectionList?
- Как использовать Canvas?
- Layouts
- Как использовать виджеты для описания свойств вёрстки?
- Как позиционировать виджеты при вёрстке?
- Стилизация
- Как стилизовать компоненты?
- Как использовать иконки и цвета?
- Как настраивать стилистичекую тему приложения?
- State management
- Как обновлять отображение виджетов?
- Локальное хранилище
- Как хранить key-value данные в приложении?
- Как хранить сложные данные?
- Навигация
- Как навигировать между экранами?
- Как использовать tab navigation?
- Как использовать drawer navigation?
- Жесты и обработка touch event
- Как обрабатывать клик?
- HTTP-запросы
- Как получать данные из API запросов?
- Форма ввода
- Какие виджеты использовать для ввода?
- Platform-specific code
- Как определить, на какой платформе выполняется код?
- Как вызвать нативный платформенный код?
- Отладка
- Какие есть инструменты для отладки приложения?
- Как сделать hot reload?
- Как получить доступ к меню разработчика в приложении?
- Анимация
- Что используется для анимации?
- Как добавить простую fade-in анимацию?
- Как добавить анимацию смахивания элементам списка?
- Эквиваленты компонентов
- Какие эквиваленты есть во Flutter по сравнению с React Native?
- Плагины Flutter
- Как получить доступ к GPS?
- Как получить доступ к камере?
- Как авторизоваться через Facebook?
- Как использовать Firebase?
Немного про Dart
Вопрос:
Где точка входа?
Ответ:
Функция
main()
.Отличия:
В JavaScript нет предопределённой точки входа, ею может быть любая функция, определённая разработчиком. В Dart это только
main()
.Вопрос:
Как делать вывод в консоль?
Ответ:
С помощью функции
print()
.Отличия:
В JavaScript вывод в консоль осуществляется с помощью
console.log()
.Пример:
print('Hello world!');
Вопрос:
Как создавать и присваивать переменные/поля?
Ответ:
Dart поддерживает и динамическую, и строгую типизацию. Поэтому переменные/поля создавать можно в любом удобном вам виде типизации. Присваивание происходит через одинарное =.
Отличия:
В JavaScript строгая типизация не поддерживается.
Дополнительная информация:
Подробнее тут.
Пример:
String name = 'dart'; // Explicitly typed as a string.
var otherName = 'Dart'; // Inferred string.
// Both are acceptable in Dart.
Вопрос:
Какое значение у переменной/поля по умолчанию?
Ответ:
null
Отличия:
В JavaScript по умолчанию
undefined
.Дополнительная информация:
Подробнее тут.
Вопрос:
Как проверять значение на
null
или на 0?Ответ:
С помощью явной проверки
==
.Отличия:
В JavaScript в рамках проверки
if
число 1 или любой non-null
объект будет эквивалентен true
. В Dart только булевое значение true
эквивалентно true
.Пример:
var myNull = null;
if (myNull == null) {
print('use "== null" to check null');
}
var zero = 0;
if (zero == 0) {
print('use "== 0" to check zero');
}
Вопрос:
Как декларировать функции?
Ответ:
В Dart функции, как и поля, могут быть с динамической или строгой типизацией возвращаемого значения. Динамически типизируемые декларируются просто именем функции и опционально параметрами, а строго типизируемые в начале ещё имеют возвращаемый тип.
Отличия:
В JavaScript функция декларируется ключевым словом
function
, далее имя и опционально — параметры.Дополнительная информация:
Подробнее тут.
Пример:
fn() {
return true;
}
// can also be written as
bool fn() {
return true;
}
Вопрос:
Какой аналог у Promise?
Ответ:
Future
Дополнительная информация:
Dart, как и JavaScript, поддерживает однопоточное выполнение. Future в Dart по смыслу выполняет те же функции, что и Promise в React Native.
Подробнее тут..
Пример:
import 'dart:convert';
import 'package:http/http.dart' as http;
class Example {
Future<String> _getIPAddress() {
final url = 'https://httpbin.org/ip';
return http.get(url).then((response) {
String ip = jsonDecode(response.body)['origin'];
return ip;
});
}
}
main() {
final example = new Example();
example
._getIPAddress()
.then((ip) => print(ip))
.catchError((error) => print(error));
}
Вопрос:
Какой аналог
async
и await
?Ответ:
async
и await
Отличия:
В JavaScript async-функции возвращают Promise, в Dart — Future. await синхронно ожидает результата вызова async-функции.
Дополнительная информация:
Подробнее тут.
Пример:
import 'dart:convert';
import 'package:http/http.dart' as http;
class Example {
Future<String> _getIPAddress() async {
final url = 'https://httpbin.org/ip';
final response = await http.get(url);
String ip = jsonDecode(response.body)['origin'];
return ip;
}
}
main() async {
final example = new Example();
try {
final ip = await example._getIPAddress();
print(ip);
} catch (error) {
print(error);
}
}
Основы
Вопрос:
Как создать проект приложения на Flutter?
Ответ:
- С помощью IDE с установленными плагинами Flutter и Dart.
- С помощью команды
flutter create {projectname}
.
Отличия:
Для создания проекта в React Native используется команда
create-react-native-app {projectname}
.Дополнительная информация:
Подробнее тут.
Вопрос:
Как запустить приложение?
Ответ:
- С помощью функции run в IDE с установленными плагинами Flutter и Dart.
- С помощью команды
flutter run
.
Отличия:
Для запуска приложения в React Native используются команды
npm run
или yarn run
.Вопрос:
Как импортировать виджеты?
Ответ:
Во Flutter все виджеты разбиты по пакетам, поэтому достаточно импортировать пакет, чтобы использовать его виджеты.
Отличия:
В React Native необходимо импортировать каждый виджет.
Пример:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/my_widgets.dart';
Вопрос:
Как использовать виджеты и вложенность для построения дерева виджетов?
Ответ:
Во Flutter почти всё — виджеты. Даже объект приложения — виджет. Каждый виджет может быть вложенным в родительский. Комбинируя виджеты, вы строите иерархию, которая называется «деревом виджетов». Именно она в дальнейшем превращается в отображение на экране.
Пример:
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'),
),
),
);
}
}
Вопрос:
Как создавать переиспользуемые компоненты?
Ответ:
Во Flutter можно создать класс виджета и переиспользовать его. Используемые параметры являются полями класса.
Отличия:
В React Native для изменения параметров переиспользуемого элемента необходимо их пометить как props.
Дополнительная информация:
Параметры в конструкторе могут декларироваться двумя способами: обычным перечислением и опциональным, с помощью фигурных скобок
{}
. Отличия проявляются при использовании параметров для вызова конструктора.При обычном перечислении все параметры являются обязательными, и при вызове конструктора нельзя указать, какой конкретно параметр передаётся, т.к. они обусловлены порядком следования.
При опциональном можно использовать любой порядок передачи параметров, но необходимо указать, какой конкретно параметр передаётся или не передавать их вовсе. Чтобы обозначить опциональный параметр как обязательный, нужно использовать аннотацию
@required
.Пример:
class CustomCard extends StatelessWidget {
CustomCard({@required this.index, @required
this.onPress});
final index;
final Function onPress;
@override
Widget build(BuildContext context) {
return Card(
child: Column(
children: <Widget>[
Text('Card $index'),
FlatButton(
child: const Text('Press'),
onPressed: this.onPress,
),
],
)
);
}
}
...
// Usage
CustomCard(
index: index,
onPress: () {
print('Card $index');
},
)
...
Структура проекта и ресурсы
Вопрос:
Где начинать писать код?
Ответ:
В
{projectname}/lib/main.dart
.Вопрос:
Какая структура файлов в проекте Flutter?
Ответ:
┬
└ projectname
┬
├ android - файлы для Android сборки.
├ build - iOS и Android результаты сборки.
├ ios - файлы для iOS сборки.
├ lib - Dart файлы.
┬
└ src - Contains additional source files.
└ main.dart - точка входа в приложение.
├ test - Файлы тестов.
└ pubspec.yaml - Описание зависимостей и ресурсов приложения.
Аналог package.json файла в React Native.
Вопрос:
Где хранить ресурсы и ассеты и как их использовать?
Ответ:
Ссылки на ресурсы и зависимости проекта хранятся в файле
pubspec.yaml
. Фактически располагать их можно в любой папке внутри lib
, главное, указать к ним путь в pubspec.yaml
. Использовать их в проекте можно либо с помощью специализированных виджетов, например AssetImage, либо напрямую, с помощью AssetBundle.Дополнительная информация:
Подробнее тут.
Пример:
Декларирование ассетов в
pubspec.yaml
flutter:
assets:
- assets/my_icon.png
- assets/background.png
Использование в коде
image: AssetImage('assets/background.png'),
Вопрос:
Как загружать изображения из сети?
Ответ:
С помощью Image.network.
Пример:
body: Image.network(
'https://flutter.io/images/owl.jpg',
Вопрос:
Как подключать сторонние пакеты и плагины?
Ответ:
С помощью
dependencies
в pubspec.yaml
.Отличия:
В React Native для добавления зависимостей используются команды
yarn add {package-name}
или npm install --save {package-name}
.Дополнительная информация:
Использование пакетов
Разработка пакетов и плагинов
Популярные плагины для Flutter
Пример:
dependencies:
flutter:
sdk: flutter
google_sign_in: ^3.0.3
Views
Вопрос:
Какой аналог у View контейнера?
Ответ:
Все базовые виджеты для вёрcтки, например Container, Column, Row и Center.
Дополнительная информация:
Подробнее тут.
Вопрос:
Какой аналог FlatList или SelectionList?
Ответ:
ListView
Пример:
var data = [ ... ];
ListView.builder(
itemCount: data.length,
itemBuilder: (context, int index) {
return Text(
data[index],
);
},
)
Вопрос:
Как использовать Canvas?
Ответ:
С помощью классов CustomPaint и CustomPainter.
Отличия:
В React Native нет возможности рисовать с помощью Canvas из коробки. Есть сторонние плагины, например
react-native-canvas
.Пример:
class MyCanvasPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
Paint paint = Paint();
paint.color = Colors.amber;
canvas.drawCircle(Offset(100.0, 200.0), 40.0, paint);
Paint paintRect = Paint();
paintRect.color = Colors.lightBlue;
Rect rect = Rect.fromPoints(Offset(150.0, 300.0), Offset(300.0, 400.0));
canvas.drawRect(rect, paintRect);
}
bool shouldRepaint(MyCanvasPainter oldDelegate) => false;
bool shouldRebuildSemantics(MyCanvasPainter oldDelegate) => false;
}
class _MyCanvasState extends State<MyCanvas> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomPaint(
painter: MyCanvasPainter(),
),
);
}
}
Layouts
Вопрос:
Как использовать виджеты для описания свойств вёрстки?
Ответ:
Во Flutter для свойств вёрстки используются специализированные виджеты, например:
Column, Row, Padding, Align, Stack.
Отличия:
В React Native чаще всего свойства вёрстки можно задать с помощью
props
.Дополнительная информация:
Полный список виджетов
Вопрос:
Как позиционировать виджеты при вёрстке?
Ответ:
Для абсолютного позиционирования используется виджет Stack. Для относительного — различные виджеты и их комбинации, которые можно найти здесь.
Пример:
Stack(
alignment: const Alignment(0.6, 0.6),
children: <Widget>[
CircleAvatar(
backgroundImage: NetworkImage(
'https://avatars3.githubusercontent.com/u/14101776?v=4'),
),
Container(
decoration: BoxDecoration(
color: Colors.black45,
),
child: Text('Flutter'),
),
],
)
Стилизация
Вопрос:
Как стилизовать компоненты?
Ответ:
Чаще всего у виджетов есть свойство
style
.Отличия:
В React Native для стилизации используется
stylesheets.create
.Пример:
var textStyle = TextStyle(fontSize: 32.0, color: Colors.cyan, fontWeight:
FontWeight.w600);
...
Center(
child: Column(
children: <Widget>[
Text(
'Sample text',
style: textStyle,
),
Padding(
padding: EdgeInsets.all(20.0),
child: Icon(Icons.lightbulb_outline,
size: 48.0, color: Colors.redAccent)
),
],
),
)
Вопрос:
Как использовать иконки и цвета?
Ответ:
С помощью классов Icons и Colors.
Отличия:
В React Native нет поддержки иконок из коробки.
Пример:
Icons
Icon(Icons.lightbulb_outline, color: Colors.redAccent)
Colors
class SampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
textSelectionColor: Colors.red
),
home: SampleAppPage(),
);
}
}
Вопрос:
Как настраивать стилистичекую тему приложения?
Ответ:
С помощью класса ThemeData.
Отличия:
В React Native общая тема настривается через
stylesheets
.Пример:
@override
Widget build(BuildContext context) {
return Theme(
data: ThemeData(
primaryColor: Colors.cyan,
brightness: brightness,
),
child: Scaffold(
backgroundColor: Theme.of(context).primaryColor,
...
...
),
);
}
State management
Вопрос:
Как обновлять отображение виджетов?
Ответ:
Используя StatefulWidget и его State. Во Flutter есть 2 вида виджетов: StatelessWidget и StatefulWidget. Они работают одинаково, отличие только в состоянии при рендеринге.
Отличия:
StatelessWidget имеет неизменное состояние. Подойдёт для отображения текста, логотипа и т.д. Т.е. если элемент на экране не должен изменяться за всё время отображения, значит, он вам подходит. Его можно использовать и как контейнер для виджетов с изменяемым состоянием.
StatefulWidget имеет состояние State, в котором хранится информация о текущем состоянии. Если вы хотите изменить элемент на экране при выполнении какого-то действия (пришёл ответ с сервера, пользователь нажал на кнопку и т.д.) — это ваш вариант.
Пример:
1) StatelessWidget — Text
Text(
'I like Flutter!',
style: TextStyle(fontWeight: FontWeight.bold),
);
2) StatefulWidget — при нажатии на кнопку (FloatingActionButton) текст в виджете Text меняется с I Like Flutter на Flutter is Awesome!
import 'package:flutter/material.dart';
void main() {
runApp(SampleApp());
}
class SampleApp extends StatelessWidget {
// Этот виджет корневой в приложении.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: SampleAppPage(),
);
}
}
class SampleAppPage extends StatefulWidget {
SampleAppPage({Key key}) : super(key: key);
@override
_SampleAppPageState createState() => _SampleAppPageState();
}
class _SampleAppPageState extends State<SampleAppPage> {
// дефолтный текст
String textToShow = "Мне нравится Flutter";
void _updateText() {
setState(() {
// обновление текста
textToShow = "Flutter крутой!";
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: Center(child: Text(textToShow)),
floatingActionButton: FloatingActionButton(
onPressed: _updateText,
tooltip: 'Обновить текст',
child: Icon(Icons.update),
),
);
}
}
Локальное хранилище
Вопрос:
Как хранить
key-value
данные в приложении?Ответ:
С помощью плагина shared_preferences.
Отличия:
В React Native используется AsyncStorage.
Пример:
Подключение зависимости
dependencies:
flutter:
sdk: flutter
shared_preferences: ^0.4.3
Использование
SharedPreferences prefs = await SharedPreferences.getInstance();
_counter = prefs.getInt('counter');
prefs.setInt('counter', ++_counter);
setState(() {
_counter = _counter;
});
Вопрос:
Как хранить сложные данные?
Ответ:
С помощью плагинов БД, например, sqflite или hive.
Навигация
Вопрос:
Как навигировать между экранами?
Ответ:
Для навигации между экранами используются классы Navigator и Route.
Отличия:
В React Native используются StackNavigator, TabNavigator и DrawerNavigator.
Во Flutter есть два способа навигации:
- описать Map с именами Route;
- напрямую навигировать к Route.
Navigator может сделать push() или pop() указанному вами маршруту.
Пример:
void main() {
runApp(CupertinoApp(
home: MyAppHome(), // becomes the route named '/'
routes: <String, WidgetBuilder> {
'/a': (BuildContext context) => MyPage(title: 'page A'),
'/b': (BuildContext context) => MyPage(title: 'page B'),
'/c': (BuildContext context) => MyPage(title: 'page C'),
},
));
}
Navigator.of(context).pushNamed('/b');
Вопрос:
Как использовать tab navigation?
Ответ:
С помощью классов: TabController, TabBar, Tab и TabBarView.
Отличия:
В React Native используются createBottomTabNavigator и TabNavigation.
Дополнительная информация:
- TabController координирует TabBar и TabBarView;
- TabBar отображает горизонтальный список табов;
- Tab отображает таб;
- TabBarView отображает контент выбранного таба.
Подробнее тут..
Пример:
class _NavigationHomePageState extends State<NavigationHomePage> with SingleTickerProviderStateMixin {
TabController controller=TabController(length: 2, vsync: this);
@override
Widget build(BuildContext context) {
return Scaffold(
bottomNavigationBar: Material (
child: TabBar(
tabs: <Tab> [
Tab(icon: Icon(Icons.person),)
Tab(icon: Icon(Icons.email),),
],
controller: controller,
),
color: Colors.blue,
),
body: TabBarView(
children: <Widget> [
home.homeScreen(),
tabScreen.tabScreen()
],
controller: controller,
)
);
}
}
Вопрос:
Как использовать drawer navigation?
Ответ:
С помощью класса Drawer.
Отличия:
В React Native используются createDrawerNavigator и DrawerNavigation.
Пример:
@override
Widget build(BuildContext context) {
return Scaffold(
drawer: Drawer(
child: ListTile(
leading: Icon(Icons.change_history),
title: Text('Screen2'),
onTap: () {
Navigator.of(context).pushNamed('/b');
},
),
elevation: 20.0,
),
appBar: AppBar(
title: Text('Home'),
),
body: Container(),
);
}
Жесты и обработка touch event
Вопрос:
Как обрабатывать клик?
Ответ:
Если виджет поддерживает клики, то в
onPressed()
. Если нет, то с помощью виджета GestureDetector.Отличия:
В React Native для этого используются PanResponder или Touchable.
Пример:
GestureDetector(
child: Scaffold(
appBar: AppBar(
title: Text('Gestures'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Tap, Long Press, Swipe Horizontally or Vertically '),
],
)
),
),
onTap: () {
print('Tapped');
},
onLongPress: () {
print('Long Pressed');
},
onVerticalDragEnd: (DragEndDetails value) {
print('Swiped Vertically');
},
onHorizontalDragEnd: (DragEndDetails value) {
print('Swiped Horizontally');
},
);
HTTP-запросы
Вопрос:
Как получать данные из API запросов?
Ответ:
С помощью http-плагина.
Отличия:
В React Native используется
fetch API
.Пример:
Подключение плагина
dependencies:
flutter:
sdk: flutter
http: <latest_version>
Получение данных
final url = Uri.https('httpbin.org', 'ip');
final httpClient = HttpClient();
_getIPAddress() async {
var request = await httpClient.getUrl(url);
var response = await request.close();
var responseBody = await response.transform(utf8.decoder).join();
String ip = jsonDecode(responseBody)['origin'];
setState(() {
_ipAddress = ip;
});
}
Форма ввода
Вопрос:
Какие виджеты использовать для ввода?
Ответ:
TextField в связке с TextEditingController или TextFormField.
Отличия:
В React Native используется TextInput.
TextFormField отличается от TextField встроенной валидацией и логикой сохранения значений в поля.
Пример:
TextField
final TextEditingController _controller = TextEditingController();
...
TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Type something', labelText: 'Text Field '
),
),
RaisedButton(
child: Text('Submit'),
onPressed: () {
showDialog(
context: context,
child: AlertDialog(
title: Text('Alert'),
content: Text('You typed ${_controller.text}'),
),
);
},
),
)
TextFormField
final formKey = GlobalKey<FormState>();
...
Form(
key:formKey,
child: Column(
children: <Widget>[
TextFormField(
validator: (value) => !value.contains('@') ? 'Not a valid email.' : null,
onSaved: (val) => _email = val,
decoration: const InputDecoration(
hintText: 'Enter your email',
labelText: 'Email',
),
),
RaisedButton(
onPressed: _submit,
child: Text('Login'),
),
],
),
)
void _submit() {
final form = formKey.currentState;
if (form.validate()) {
form.save();
showDialog(
context: context,
child: AlertDialog(
title: Text('Alert'),
content: Text('Email: $_email, password: $_password'),
)
);
}
}
Platform-specific code
Вопрос:
Как определить, на какой платформе выполняется код?
Ответ:
С помощью класса поля
platform
в Theme или класса Platform.Пример:
Поле
platform
if (Theme.of(context).platform == TargetPlatform.iOS) {
return 'iOS';
} else if (Theme.of(context).platform == TargetPlatform.android) {
return 'android';
} else if (Theme.of(context).platform == TargetPlatform.fuchsia) {
return 'fuchsia';
} else {
return 'not recognised ';
}
Класс Platform
if (Platform.isIOS) {
return 'iOS';
} else if (Platform.isAndroid) {
return 'android';
} else if (Platform.isFuchsia) {
return 'fuchsia';
} else {
return 'not recognised ';
}
Вопрос:
Как вызвать нативный платформенный код?
Ответ:
Через MethodChannel.
Дополнительная информация:
Подробнее тут..
Отладка
Вопрос:
Какие есть инструменты для отладки приложения?
Ответ:
DevTools.
Вопрос:
Как сделать
hot reload
?Ответ:
Если приложение запускалось из IntelliJ IDE или Android Studio, то сочетанием
⌘s/ctrl-s
или нажатием на иконку hot reload
. Если запускалось из терминала, то вводом буквы r
.Отличия:
В React Native для iOS-эмуляторов используется сочетание
⌘R/Ctrl+R
, для Android — двойное R
.Вопрос:
Как получить доступ к меню разработчика в приложении?
Ответ:
Если запуск был из IDE, то с помощью инструментов IDE. Если из консоли, то с помощью ввода h.
Отличия:
В React Native используются
⌘D/Ctrl+D
для iOS эмуляторов и ⌘M/Ctrl+M
для Android.Дополнительная информация:
Полный список команд
Действие | Команда в терминале | Функции и поля |
Иерархия виджетов | w | debugDumpApp() |
Дерево рендеринга | t | debugDumpRenderTree() |
Слои | L | debugDumpLayerTree() |
Accessibility | S (traversal order) or U (inverse hit test order) | debugDumpSemantics() |
Инспектор виджетов | i | WidgetsApp.showWidgetInspectorOverride |
Отображение линий построения | p | debugPaintSizeEnabled |
Симуляция разных ОС | o | defaultTargetPlatform |
Перфоманс | P | WidgetsApp. showPerformanceOverlay |
Скриншот flutter.png | s | |
Закрытие приложения | q |
Анимация
Вопрос:
Что используется для анимации?
Ответ:
Animation и AnimationController.
Отличия:
В React Native используется
Animation API
.Дополнительная информация:
Подробнее тут.
Вопрос:
Как добавить простую
fade-in
анимацию?Ответ:
С помощью FadeTransition.
Пример:
import 'package:flutter/material.dart';
void main() {
runApp(Center(child: LogoFade()));
}
class LogoFade extends StatefulWidget {
_LogoFadeState createState() => _LogoFadeState();
}
class _LogoFadeState extends State<LogoFade> with TickerProviderStateMixin {
Animation animation;
AnimationController controller;
initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 3000), vsync: this);
final CurvedAnimation curve =
CurvedAnimation(parent: controller, curve: Curves.easeIn);
animation = Tween(begin: 0.0, end: 1.0).animate(curve);
controller.forward();
}
Widget build(BuildContext context) {
return FadeTransition(
opacity: animation,
child: Container(
height: 300.0,
width: 300.0,
child: FlutterLogo(),
),
);
}
dispose() {
controller.dispose();
super.dispose();
}
}
Вопрос:
Как добавить анимацию смахивания элементам списка?
Ответ:
С помощью Dismissible.
Пример:
child: Dismissible(
key: key,
onDismissed: (DismissDirection dir) {
cards.removeLast();
},
child: Container(
...
),
),
Эквиваленты компонентов
Вопрос:
Какие эквиваленты есть во Flutter по сравнению с React Native?
Ответ:
React Native | Flutter |
Button | RaisedButton |
Button | FlatButton |
ScrollView | ListView |
FlatList | ListView.builder() |
Image | Image |
Modal | ModalRoute |
ActivityIndicator | CircularProgressIndicator |
ActivityIndicator | LinearProgressIndicator |
RefreshControl | RefreshIndicator |
View | Container |
View | Column |
View | Row |
View | Center |
View | Padding |
TouchableOpacity | GestureDetector |
TextInput | TextInput |
Text | Text |
Switch | Switch |
Slider | Slider |
Плагины Flutter
Вопрос:
Как получить доступ к GPS?
Ответ:
С помощью плагина geolocator.
Вопрос:
Как получить доступ к камере?
Ответ:
С помощью плагина image_picker.
Вопрос:
Как авторизоваться через Facebook?
Ответ:
С помощью плагина flutter_facebook_login.
Вопрос:
Как использовать Firebase?
Ответ:
Firebase поддерживает Flutter first party plugins:
- firebase_admob для Firebase AdMob;
- firebase_analytics для Firebase Analytics;
- firebase_auth для Firebase Auth;
- firebase_database для Firebase RTDB;
- firebase_storage для Firebase Cloud Storage;
- firebase_messaging для Firebase Messaging (FCM);
- flutter_firebase_ui для быстрой интеграции Firebase Auth (Facebook, Google, Twitter and e-mail);
- cloud_firestore для Firebase Cloud Firestore.
Вот, пожалуй, ответы на основные вопросы. Надеюсь, моя интерпретация была для вас полезна, и если вы ещё не пишите на Flutter, то хотя бы задумались об этом. А это значит, что в полку Flutter разработчиков, возможно, будет прибавление, и мы вместе будем делать мир лучше через призму разработанных удобных и быстрых приложений! Да не сломает React ваш Native!