Flutter - Краткое руководство
В целом разработка мобильного приложения - сложная и ответственная задача. Для разработки мобильного приложения доступно множество фреймворков. Android предоставляет собственный фреймворк на основе языка Java, а iOS предоставляет собственный фреймворк на основе языка Objective-C / Swift.
Однако для разработки приложения, поддерживающего обе ОС, нам нужно кодировать на двух разных языках с использованием двух разных фреймворков. Чтобы преодолеть эту сложность, существуют мобильные платформы, поддерживающие обе ОС. Эти структуры варьируются от простой гибридной платформы мобильных приложений на основе HTML (которая использует HTML для пользовательского интерфейса и JavaScript для логики приложения) до сложной языковой структуры (которая выполняет тяжелую работу по преобразованию кода в собственный код). Независимо от их простоты или сложности, эти фреймворки всегда имеют множество недостатков, одним из основных недостатков является их низкая производительность.
В этом сценарии Flutter - простой и высокопроизводительный фреймворк, основанный на языке Dart, обеспечивает высокую производительность за счет рендеринга пользовательского интерфейса непосредственно на холсте операционной системы, а не через родную структуру.
Flutter также предлагает множество готовых к использованию виджетов (UI) для создания современного приложения. Эти виджеты оптимизированы для мобильной среды, и разработать приложение с использованием виджетов так же просто, как разработать HTML.
Если быть точным, приложение Flutter само по себе является виджетом. Виджеты Flutter также поддерживают анимацию и жесты. Логика приложения основана на реактивном программировании. Виджет может опционально иметь состояние. Изменяя состояние виджета, Flutter автоматически (реактивное программирование) сравнивает состояние виджета (старое и новое) и визуализирует виджет только с необходимыми изменениями вместо повторного рендеринга всего виджета.
Мы обсудим полную архитектуру в следующих главах.
Особенности Flutter
Фреймворк Flutter предлагает разработчикам следующие функции:
Современный и реактивный фреймворк.
Использует язык программирования Dart, и его очень легко выучить.
Быстрое развитие.
Красивые и плавные пользовательские интерфейсы.
Огромный каталог виджетов.
Выполняет один и тот же пользовательский интерфейс для нескольких платформ.
Приложение с высокой производительностью.
Преимущества Flutter
Flutter поставляется с красивыми и настраиваемыми виджетами для обеспечения высокой производительности и выдающегося мобильного приложения. Он отвечает всем индивидуальным потребностям и требованиям. Помимо этого, Flutter предлагает еще много преимуществ, как указано ниже -
Dart имеет большой репозиторий программных пакетов, который позволяет вам расширять возможности вашего приложения.
Разработчикам необходимо написать единую базу кода для обоих приложений (как для платформ Android, так и для iOS). В будущем Flutter может быть расширен и на другие платформы.
Flutter требует меньшего тестирования. Из-за единой кодовой базы достаточно, если мы напишем автоматические тесты один раз для обеих платформ.
Простота Flutter делает его хорошим кандидатом для быстрой разработки. Возможности настройки и расширения делают его еще более мощным.
С Flutter разработчики получают полный контроль над виджетами и их расположением.
Flutter предлагает отличные инструменты для разработчиков с потрясающей горячей перезагрузкой.
Недостатки Flutter
Несмотря на множество преимуществ, флаттер имеет следующие недостатки:
Поскольку он написан на языке Dart, разработчику необходимо изучить новый язык (хотя его легко выучить).
Современный фреймворк пытается максимально разделить логику и пользовательский интерфейс, но во Flutter пользовательский интерфейс и логика перемешаны. Мы можем преодолеть это, используя интеллектуальное кодирование и модуль высокого уровня для разделения пользовательского интерфейса и логики.
Flutter - еще один фреймворк для создания мобильных приложений. Разработчикам сложно выбрать правильные инструменты разработки в густонаселенном сегменте.
Эта глава подробно расскажет вам об установке Flutter на ваш локальный компьютер.
Установка в Windows
В этом разделе давайте посмотрим, как установить Flutter SDK и его требования в системе Windows.
Step 1 - Перейти по URL,https://flutter.dev/docs/get-started/install/windowsи загрузите последнюю версию Flutter SDK. По состоянию на апрель 2019 года это версия 1.2.1, а файл - flutter_windows_v1.2.1-stable.zip.
Step 2 - Разархивируйте zip-архив в папку, скажем C: \ flutter \
Step 3 - Обновите системный путь, чтобы включить каталог бункера флаттера.
Step 4 - Flutter предоставляет инструмент, доктор флаттера, чтобы проверить выполнение всех требований по развитию флаттера.
flutter doctor
Step 5 - Выполнение указанной выше команды проанализирует систему и покажет отчет, как показано ниже -
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, v1.2.1, on Microsoft Windows [Version
10.0.17134.706], locale en-US)
[√] Android toolchain - develop for Android devices (Android SDK version
28.0.3)
[√] Android Studio (version 3.2)
[√] VS Code, 64-bit edition (version 1.29.1)
[!] Connected device
! No devices available
! Doctor found issues in 1 category.
В отчете говорится, что все инструменты разработки доступны, но устройство не подключено. Мы можем исправить это, подключив устройство Android через USB или запустив эмулятор Android.
Step 6 - Установите последнюю версию Android SDK, если сообщит врач-флаттер.
Step 7 - Установите последнюю версию Android Studio, если сообщит врач-флаттер.
Step 8 - Запустите эмулятор Android или подключите к системе настоящее устройство Android.
Step 9- Установите плагин Flutter and Dart для Android Studio. Он предоставляет шаблон запуска для создания нового приложения Flutter, возможность запуска и отладки приложения Flutter в самой студии Android и т. Д.
Откройте Android Studio.
Щелкните Файл → Настройки → Плагины.
Выберите плагин Flutter и нажмите Установить.
Нажмите Да, когда будет предложено установить плагин Dart.
Перезагрузите студию Android.
Установка в MacOS
Чтобы установить Flutter на MacOS, вам нужно будет выполнить следующие шаги:
Step 1 - Перейти по URL,https://flutter.dev/docs/get-started/install/macosи загрузите последнюю версию Flutter SDK. По состоянию на апрель 2019 года это версия 1.2.1, а файл - flutter_macos_v1.2.1- stable.zip.
Step 2 - Разархивируйте zip-архив в папку, скажем / path / to / flutter
Step 3 - Обновите системный путь, чтобы включить каталог бункера flutter (в файле ~ / .bashrc).
> export PATH = "$PATH:/path/to/flutter/bin"
Step 4 - Включите обновленный путь в текущем сеансе, используя команду ниже, а затем также проверьте его.
source ~/.bashrc
source $HOME/.bash_profile
echo $PATH
Flutter предоставляет инструмент, доктор флаттера, чтобы проверить выполнение всех требований к развитию флаттера. Он похож на аналог Windows.
Step 5 - Установите последнюю версию XCode, если сообщит врач-флаттер
Step 6 - Установите последнюю версию Android SDK, если сообщит врач-флаттер.
Step 7 - Установите последнюю версию Android Studio, если сообщит врач-флаттер.
Step 8 - Запустите эмулятор Android или подключите к системе настоящее устройство Android для разработки приложения для Android.
Step 9 - Откройте симулятор iOS или подключите к системе реальное устройство iPhone для разработки приложения iOS.
Step 10- Установите плагин Flutter and Dart для Android Studio. Он предоставляет шаблон запуска для создания нового приложения Flutter, возможность запуска и отладки приложения Flutter в самой студии Android и т. Д.
Откройте Android Studio
Нажмите Preferences → Plugins
Выберите плагин Flutter и нажмите Установить.
Нажмите Да, когда будет предложено установить плагин Dart.
Перезагрузите студию Android.
В этой главе давайте создадим простое приложение Flutter, чтобы понять основы создания приложения Flutter в Android Studio.
Step 1 - Откройте Android Studio
Step 2- Создать проект Flutter. Для этого нажмитеFile → New → New Flutter Project
Step 3- Выберите приложение Flutter. Для этого выберитеFlutter Application и нажмите Next.
Step 4 - Настройте приложение, как показано ниже, и нажмите Next.
Название проекта: hello_app
Путь к SDK Flutter: <path_to_flutter_sdk>
Расположение проекта: <path_to_project_folder>
Описание: Flutter based hello world application
Step 5 - Настроить проект.
Установите домен компании как flutterapp.tutorialspoint.com и нажмите Finish.
Step 6 - Введите домен компании.
Android Studio создает полностью работающее приложение Flutter с минимальной функциональностью. Давайте проверим структуру приложения, а затем изменим код для выполнения нашей задачи.
Структура приложения и его цель следующие:
Здесь объясняются различные компоненты структуры приложения -
android - Автоматически сгенерированный исходный код для создания приложения для Android
ios - Автоматически сгенерированный исходный код для создания приложения ios
lib - Основная папка, содержащая код Dart, написанный с использованием фреймворка Flutter.
ib/main.dart - Точка входа в приложение Flutter
test - Папка с кодом Dart для тестирования приложения Flutter
test/widget_test.dart - Пример кода
.gitignore - Файл контроля версий Git
.metadata - автоматически генерируется инструментами флаттера
.packages - автоматически сгенерирован для отслеживания пакетов флаттера
.iml - файл проекта, используемый студией Android
pubspec.yaml - Используется Pub, Менеджер пакетов Flutter
pubspec.lock - Автоматически сгенерирован менеджером пакетов Flutter, Pub
README.md - Файл описания проекта, написанный в формате Markdown
Step 7- Замените код дротика в файле lib / main.dart приведенным ниже кодом -
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello World Demo Application',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Home page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: Center(
child:
Text(
'Hello World',
)
),
);
}
}
Давайте разберемся с кодом дротика построчно.
Line 1- импортирует флаттер пакет, материал . Материал представляет собой флаттер-пакет для создания пользовательского интерфейса в соответствии с рекомендациями по дизайну материалов, установленными Android.
Line 3- Это точка входа в приложение Flutter. Вызывает функцию runApp и передает ей объект класса MyApp . Назначение функции runApp - прикрепить данный виджет к экрану.
Line 5-17- Виджет используется для создания пользовательского интерфейса в фреймворке флаттера. StatelessWidget - это виджет, который не поддерживает какое-либо состояние виджета. MyApp расширяет StatelessWidget и переопределяет его метод сборки . Цель метода сборки - создать часть пользовательского интерфейса приложения. Здесь метод сборки использует MaterialApp , виджет для создания пользовательского интерфейса корневого уровня приложения. У него три свойства - заголовок, тема и дом .
title - это заголовок приложения
theme - это тема виджета. Здесь мы устанавливаем синий цвет в качестве общего цвета приложения с помощью класса ThemeData и его свойства primarySwatch .
home - это внутренний пользовательский интерфейс приложения, в котором мы устанавливаем другой виджет, MyHomePage
Line 19 - 38- MyHomePage совпадает с MyApp, за исключением того, что возвращает виджет Scaffold . Scaffold - это виджет верхнего уровня рядом с виджетом MaterialApp, используемый для создания UI-совместимого дизайна материалов. Он имеет два важных свойства: appBar для отображения заголовка приложения и тела для отображения фактического содержимого приложения. AppBar - еще один виджет для рендеринга заголовка приложения, и мы использовали его в свойстве appBar . В свойстве body мы использовали центральный виджет, который центрирует его дочерний виджет. Текст - это последний и самый внутренний виджет для отображения текста, и он отображается в центре экрана.
Step 8 - Теперь запустите приложение, используя, Run → Run main.dart
Step 9 - Наконец, вывод приложения выглядит следующим образом -
В этой главе давайте обсудим архитектуру фреймворка Flutter.
Виджеты
Основная концепция фреймворка Flutter: In Flutter, Everything is a widget. Виджеты - это в основном компоненты пользовательского интерфейса, используемые для создания пользовательского интерфейса приложения.
Во Flutter приложение само по себе является виджетом. Приложение является виджетом верхнего уровня, и его пользовательский интерфейс создается с использованием одного или нескольких дочерних элементов (виджетов), которые снова создаются с использованием своих дочерних виджетов. Этотcomposability Функция помогает нам создавать пользовательский интерфейс любой сложности.
Например, иерархия виджетов приложения hello world (созданного в предыдущей главе) указана на следующей диаграмме:
Здесь следует отметить следующие моменты:
MyApp - это виджет, созданный пользователем, и он создается с использованием собственного виджета Flutter, MaterialApp .
В MaterialApp есть свойство home для указания пользовательского интерфейса домашней страницы, который снова является созданным пользователем виджетом MyHomePage .
MyHomePage построен с использованием другого собственного виджета Flutter , Scaffold.
Scaffold имеет два свойства - body и appBar.
body используется для указания своего основного пользовательского интерфейса, а appBar используется для указания пользовательского интерфейса заголовка.
Пользовательский интерфейс заголовка создается с использованием собственного виджета Flutter, пользовательский интерфейс AppBar и Body создается с использованием виджета Center .
У виджета « Центр» есть свойство « Дочерний» , которое ссылается на фактический контент, и он создается с использованием текстового виджета.
Жесты
Виджеты Flutter поддерживают взаимодействие через специальный виджет GestureDetector . GestureDetector - это невидимый виджет, имеющий возможность фиксировать действия пользователя, такие как касание, перетаскивание и т. Д., Своего дочернего виджета. Многие собственные виджеты Flutter поддерживают взаимодействие с помощью GestureDetector . Мы также можем включить интерактивную функцию в существующий виджет, составив его с помощью виджета GestureDetector . Мы изучим жесты отдельно в следующих главах.
Понятие государства
Виджеты Flutter поддерживают поддержку состояния , предоставляя специальный виджет StatefulWidget . Виджет должен быть производным от виджета StatefulWidget для поддержки обслуживания состояния, а все остальные виджеты должны быть производными от StatefulWidget . Виджеты Flutterreactiveв родном. Это похоже на responsejs, и StatefulWidget будет автоматически обновляться при изменении его внутреннего состояния. Повторный рендеринг оптимизирован за счет обнаружения разницы между старым и новым пользовательским интерфейсом виджета и визуализации только необходимых изменений.
Слои
Самая важная концепция фреймворка Flutter заключается в том, что фреймворк сгруппирован по нескольким категориям с точки зрения сложности и четко упорядочен по уровням убывающей сложности. Слой строится с использованием непосредственно следующего за ним уровня. Самый верхний слой - это виджет, специфичный для Android и iOS . На следующем слое есть все собственные виджеты флаттера. Следующий слой - это слой рендеринга , который является компонентом рендеринга низкого уровня и рендерит все в приложении flutter. Уровни сводятся к основному коду платформы
Общий обзор слоя во Flutter указан на диаграмме ниже -
Следующие пункты обобщают архитектуру Flutter -
Во Flutter все является виджетом, а сложный виджет состоит из уже существующих виджетов.
Интерактивные функции могут быть включены при необходимости с помощью виджета GestureDetector .
Состояние виджета можно при необходимости поддерживать с помощью виджета StatefulWidget .
Flutter предлагает многоуровневый дизайн, так что любой слой может быть запрограммирован в зависимости от сложности задачи.
Мы подробно обсудим все эти концепции в следующих главах.
Dart - это язык программирования общего назначения с открытым исходным кодом. Первоначально он разработан Google. Dart - объектно-ориентированный язык с синтаксисом в стиле C. Он поддерживает такие концепции программирования, как интерфейсы, классы, в отличие от других языков программирования, Dart не поддерживает массивы. Коллекции Dart могут использоваться для репликации структур данных, таких как массивы, обобщения и необязательная типизация.
Следующий код показывает простую программу Dart -
void main() {
print("Dart language is easy to learn");
}
Переменные и типы данных
Переменная называется местом хранения, а Типы данных просто относятся к типу и размеру данных, связанных с переменными и функциями.
Дарт использует ключевое слово var для объявления переменной. Синтаксис var определяется ниже,
var name = 'Dart';
Окончательное и Const ключевым слово используется для объявления констант. Они определены ниже -
void main() {
final a = 12;
const pi = 3.14;
print(a);
print(pi);
}
Язык Dart поддерживает следующие типы данных -
Numbers - Используется для представления числовых литералов - Integer и Double.
Strings- Он представляет собой последовательность символов. Строковые значения указываются в одинарных или двойных кавычках.
Booleans- Dart использует ключевое слово bool для представления логических значений - истина и ложь.
Lists and Maps- Используется для представления коллекции объектов. Простой список можно определить, как показано ниже -.
void main() {
var list = [1,2,3,4,5];
print(list);
}
Список, показанный выше, производит список [1,2,3,4,5].
Карту можно определить, как показано здесь -
void main() {
var mapping = {'id': 1,'name':'Dart'};
print(mapping);
}
Dynamic- Если тип переменной не определен, то ее тип по умолчанию - динамический. В следующем примере показана переменная динамического типа -
void main() {
dynamic name = "Dart";
print(name);
}
Принятие решений и циклы
Блок принятия решения оценивает условие перед выполнением инструкций. Dart поддерживает операторы If, If..else и switch.
Циклы используются для повторения блока кода до тех пор, пока не будет выполнено определенное условие. Dart поддерживает циклы for, for..in, while и do.. while.
Давайте разберемся на простом примере использования управляющих операторов и циклов -
void main() {
for( var i = 1 ; i <= 10; i++ ) {
if(i%2==0) {
print(i);
}
}
}
Приведенный выше код печатает четные числа от 1 до 10.
Функции
Функция - это группа операторов, которые вместе выполняют определенную задачу. Давайте посмотрим на простую функцию в Dart, как показано здесь -
void main() {
add(3,4);
}
void add(int a,int b) {
int c;
c = a+b;
print(c);
}
Вышеупомянутая функция складывает два значения и возвращает 7 на выходе.
Объектно-ориентированного программирования
Dart - объектно-ориентированный язык. Он поддерживает функции объектно-ориентированного программирования, такие как классы, интерфейсы и т. Д.
Класс - это план для создания объектов. Определение класса включает в себя следующее -
- Fields
- Геттеры и сеттеры
- Constructors
- Functions
Теперь давайте создадим простой класс, используя приведенные выше определения -
class Employee {
String name;
//getter method
String get emp_name {
return name;
}
//setter method
void set emp_name(String name) {
this.name = name;
}
//function definition
void result() {
print(name);
}
}
void main() {
//object creation
Employee emp = new Employee();
emp.name = "employee1";
emp.result(); //function call
}
Как мы узнали в предыдущей главе, виджеты - это все во фреймворке Flutter. Мы уже узнали, как создавать новые виджеты в предыдущих главах.
В этой главе давайте разберемся с реальной концепцией создания виджетов и различных типов виджетов, доступных во фреймворке Flutter .
Давайте проверим виджет MyHomePage приложения Hello World . Код для этой цели приведен ниже -
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(this.title), ),
body: Center(child: Text( 'Hello World',)),
);
}
}
Здесь мы создали новый виджет, расширив StatelessWidget .
Обратите внимание, что StatelessWidget требует реализации только одной сборки метода в производном классе. Метод сборки получает контекстную среду, необходимую для сборки виджетов, через параметр BuildContext и возвращает создаваемый виджет.
В коде мы использовали title как один из аргументов конструктора, а также использовали Key как другой аргумент. Название используется для отображения названия и ключ используется для идентификации виджета в среде сборки.
Здесь сборки метод вызывает сборки метод строительных лесов , который , в свою очередь , называют сборки метода AppBar и Центра , чтобы построить свой пользовательский интерфейс.
Наконец, метод сборки Center вызывает метод сборки Text .
Для лучшего понимания ниже приведено визуальное представление того же самого -
Визуализация сборки виджета
Во Flutter виджеты можно сгруппировать в несколько категорий в зависимости от их функций, как указано ниже:
- Виджеты для конкретных платформ
- Виджеты макета
- Виджеты обслуживания состояния
- Независимые от платформы / базовые виджеты
Обсудим теперь подробно каждую из них.
Виджеты для конкретных платформ
У Flutter есть виджеты, специфичные для конкретной платформы - Android или iOS.
Виджеты для Android разработаны в соответствии с рекомендациями по дизайну материалов для ОС Android. Виджеты, специфичные для Android, называются виджетами материалов .
Виджеты для iOS разработаны в соответствии с Руководством Apple по интерфейсу пользователя и называются виджетами Купертино .
Вот некоторые из наиболее часто используемых виджетов материалов:
- Scaffold
- AppBar
- BottomNavigationBar
- TabBar
- TabBarView
- ListTile
- RaisedButton
- FloatingActionButton
- FlatButton
- IconButton
- DropdownButton
- PopupMenuButton
- ButtonBar
- TextField
- Checkbox
- Radio
- Switch
- Slider
- Выбор даты и времени
- SimpleDialog
- AlertDialog
Вот некоторые из наиболее часто используемых виджетов Купертино :
- CupertinoButton
- CupertinoPicker
- CupertinoDatePicker
- CupertinoTimerPicker
- CupertinoNavigationBar
- CupertinoTabBar
- CupertinoTabScaffold
- CupertinoTabView
- CupertinoTextField
- CupertinoDialog
- CupertinoDialogAction
- CupertinoFullscreenDialogTransition
- CupertinoPageScaffold
- CupertinoPageTransition
- CupertinoActionSheet
- CupertinoActivityIndicator
- CupertinoAlertDialog
- CupertinoPopupSurface
- CupertinoSlider
Виджеты макета
Во Flutter виджет можно создать, составив один или несколько виджетов. Чтобы объединить несколько виджетов в один виджет, Flutter предоставляет большое количество виджетов с функцией макета. Например, дочерний виджет можно центрировать с помощью виджета « Центр» .
Вот некоторые из популярных виджетов макета:
Container- Прямоугольная рамка, украшенная виджетами BoxDecoration с фоном, рамкой и тенью.
Center - Центрируйте его дочерний виджет.
Row - Расположите его детей по горизонтали.
Column - Расположите его дочерние элементы в вертикальном направлении.
Stack - Расположите одно над другим.
Мы подробно проверим виджеты макета в следующей главе « Введение в виджеты макета» .
Виджеты обслуживания состояния
Во Flutter все виджеты являются производными от StatelessWidget или StatefulWidget .
Виджет, производный от StatelessWidget , не имеет никакой информации о состоянии, но может содержать виджет, производный от StatefulWidget . Динамический характер приложения обусловлен интерактивным поведением виджетов и изменением состояния во время взаимодействия. Например, нажатие кнопки счетчика увеличит / уменьшит внутреннее состояние счетчика на единицу, а реактивный характер виджета Flutter будет автоматически повторно отображать виджет с использованием новой информации о состоянии.
Мы подробно изучим концепцию виджетов StatefulWidget в следующей главе «Управление состоянием» .
Независимые от платформы / базовые виджеты
Flutter предоставляет большое количество базовых виджетов для создания как простого, так и сложного пользовательского интерфейса независимо от платформы. Давайте посмотрим на некоторые из основных виджетов в этой главе.
Text
Текстовый виджет используется для отображения отрезка строки. Стиль строки можно задать с помощью свойства стиля и класса TextStyle . Пример кода для этой цели выглядит следующим образом:
Text('Hello World!', style: TextStyle(fontWeight: FontWeight.bold))
Текстовый виджет имеет специальный конструктор Text.rich , который принимает дочерний элемент типа TextSpan для указания строки с другим стилем. Виджет TextSpan является рекурсивным по своей природе и принимает TextSpan в качестве своих дочерних элементов . Пример кода для этой цели выглядит следующим образом:
Text.rich(
TextSpan(
children: <TextSpan>[
TextSpan(text: "Hello ", style:
TextStyle(fontStyle: FontStyle.italic)),
TextSpan(text: "World", style:
TextStyle(fontWeight: FontWeight.bold)),
],
),
)
Наиболее важные свойства текстового виджета следующие:
maxLines, int - Максимальное количество строк для отображения
overflow, TextOverFlow- Укажите, как обрабатывается визуальное переполнение с помощью класса TextOverFlow
style, TextStyle- Укажите стиль строки с помощью класса TextStyle
textAlign, TextAlign- Выравнивание текста по правому, левому краю , по ширине и т. Д. С использованием класса TextAlign
textDirection, TextDirection - Направление текста: слева направо или справа налево.
Image
Виджет изображения используется для отображения изображения в приложении. Виджет изображения предоставляет различные конструкторы для загрузки изображений из нескольких источников, и они следующие:
Image- Универсальный загрузчик изображений с использованием ImageProvider
Image.asset - Загрузить изображение из ресурсов проекта flutter
Image.file - Загрузить изображение из системной папки
Image.memory - Загрузить изображение из памяти
Image.Network - Загрузить изображение из сети
Самый простой способ загрузить и отобразить изображение во Flutter - это включить изображение в качестве ресурсов приложения и загрузить его в виджет по запросу.
Создайте папку с активами в папке проекта и разместите необходимые изображения.
Укажите активы в pubspec.yaml, как показано ниже -
flutter:
assets:
- assets/smiley.png
Теперь загрузите и отобразите изображение в приложении.
Image.asset('assets/smiley.png')
Полный исходный код виджета MyHomePage приложения hello world и результат показаны ниже -.
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar( title: Text(this.title), ),
body: Center( child: Image.asset("assets/smiley.png")),
);
}
}
Загруженное изображение показано ниже -
Наиболее важные свойства виджета Image следующие:
image, ImageProvider - Фактическое изображение для загрузки
width, double - Ширина изображения
height, double - Высота изображения
alignment, AlignmentGeometry - Как выровнять изображение в пределах его границ
Icon
Виджет Icon используется для отображения глифа из шрифта, описанного в классе IconData . Код для загрузки простого значка электронной почты выглядит следующим образом:
Icon(Icons.email)
Полный исходный код для его применения в приложении hello world выглядит следующим образом:
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(this.title),),
body: Center( child: Icon(Icons.email)),
);
}
}
Значок загружен, как показано ниже -
Поскольку основная концепция Flutter заключается в том, что все является виджетом , Flutter включает функциональность макета пользовательского интерфейса в сами виджеты. Flutter предоставляет довольно много специально разработанных виджетов, таких как Container, Center, Align и т. Д., Только с целью создания пользовательского интерфейса. Виджеты, создаваемые путем компоновки других виджетов, обычно используют виджеты макета. Давайте изучим концепцию макета Flutter в этой главе.
Тип макета виджетов
Виджеты макета можно сгруппировать в две отдельные категории в зависимости от их дочерних элементов:
- Виджет, поддерживающий одного ребенка
- Виджет с поддержкой нескольких дочерних элементов
Давайте познакомимся с типом виджетов и их функциями в следующих разделах.
Отдельные дочерние виджеты
В этой категории у виджетов будет только один виджет в качестве дочернего, и каждый виджет будет иметь особую функциональность макета.
Например, виджет « Центр» просто центрирует свой дочерний виджет по отношению к его родительскому виджету, а виджет- контейнер обеспечивает полную гибкость для размещения его дочернего элемента в любом заданном месте внутри него с использованием различных параметров, таких как заполнение, украшение и т. Д.
Отдельные дочерние виджеты - отличные варианты для создания высококачественных виджетов с единственной функциональностью, такой как кнопка, метка и т.
Код для создания простой кнопки с использованием виджета- контейнера следующий:
class MyButton extends StatelessWidget {
MyButton({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
border: Border(
top: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
left: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)),
right: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
bottom: BorderSide(width: 1.0, color: Color(0xFFFF000000)),
),
),
child: Container(
padding: const
EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0),
decoration: const BoxDecoration(
border: Border(
top: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
left: BorderSide(width: 1.0, color: Color(0xFFFFDFDFDF)),
right: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
bottom: BorderSide(width: 1.0, color: Color(0xFFFF7F7F7F)),
),
color: Colors.grey,
),
child: const Text(
'OK',textAlign: TextAlign.center, style: TextStyle(color: Colors.black)
),
),
);
}
}
Здесь мы использовали два виджета - виджет контейнера и виджет текста . Результатом виджета является настраиваемая кнопка, как показано ниже -
Давайте проверим некоторые из наиболее важных виджетов с одним дочерним макетом, предоставляемые Flutter -
Padding- Используется для упорядочивания дочернего виджета по заданному отступу. Здесь заполнение может быть предоставлено классом EdgeInsets .
Align- Выровняйте дочерний виджет внутри себя, используя значение свойства alignment . Значение свойства выравнивания может быть предоставлено классом FractionalOffset . Класс FractionalOffset определяет смещения в терминах расстояния от верхнего левого угла.
Некоторые из возможных значений смещений следующие:
FractionalOffset (1.0, 0.0) представляет верхний правый угол.
FractionalOffset (0,0, 1,0) представляет нижний левый угол.
Пример кода о смещениях показан ниже -
Center(
child: Container(
height: 100.0,
width: 100.0,
color: Colors.yellow, child: Align(
alignment: FractionalOffset(0.2, 0.6),
child: Container( height: 40.0, width:
40.0, color: Colors.red,
),
),
),
)
FittedBox - Он масштабирует дочерний виджет, а затем позиционирует его в соответствии с заданной посадкой.
AspectRatio - Он пытается изменить размер дочернего виджета до указанного соотношения сторон.
ConstrainedBox
Baseline
FractinallySizedBox
IntrinsicHeight
IntrinsicWidth
LiimitedBox
OffStage
OverflowBox
SizedBox
SizedOverflowBox
Transform
CustomSingleChildLayout
Наше приложение hello world использует виджеты макета на основе материалов для разработки домашней страницы. Давайте изменим наше приложение hello world, чтобы создать домашнюю страницу с использованием основных виджетов макета, как указано ниже -
Container - Общий виджет-контейнер с одним дочерним элементом, основанный на блоке, с выравниванием, заполнением, границей и полями, а также с богатыми функциями стилизации.
Center - Простой виджет-контейнер с одним дочерним элементом, который центрирует его дочерний виджет.
Измененный код виджетов MyHomePage и MyApp выглядит следующим образом:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MyHomePage(title: "Hello World demo app");
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(color: Colors.white,),
padding: EdgeInsets.all(25), child: Center(
child:Text(
'Hello World', style: TextStyle(
color: Colors.black, letterSpacing: 0.5, fontSize: 20,
),
textDirection: TextDirection.ltr,
),
)
);
}
}
Вот,
Виджет- контейнер - это виджет верхнего уровня или корневой виджет. Контейнер настраивается с помощью свойства украшения и заполнения для размещения его содержимого.
BoxDecoration имеет множество свойств, таких как цвет, граница и т. Д., Для украшения виджета « Контейнер», и здесь цвет используется для установки цвета контейнера.
обивка из контейнера виджета устанавливается с помощью dgeInsets класса, который предоставляет возможность указать значение заполнения.
Центр - это дочерний виджет виджета « Контейнер» . Опять же, Text является дочерним элементом виджета Center . Текст используется для отображения сообщения, а Центр используется для центрирования текстового сообщения относительно родительского виджета, Контейнера .
Конечным результатом приведенного выше кода является образец макета, как показано ниже -
Несколько дочерних виджетов
В этой категории у данного виджета будет несколько дочерних виджетов, и макет каждого виджета уникален.
Например, виджет « Строка» позволяет размещать дочерние элементы в горизонтальном направлении, тогда как виджет « Столбец» позволяет размещать дочерние элементы в вертикальном направлении. Комбинируя строку и столбец , можно построить виджет любого уровня сложности.
Давайте изучим некоторые из часто используемых виджетов в этом разделе.
Row - Позволяет расположить своих детей по горизонтали.
Column - Позволяет расположить своих детей вертикально.
ListView - Позволяет расположить его дочерние элементы в виде списка.
GridView - Позволяет оформить его детей как галерею.
Expanded - Используется для того, чтобы дочерние элементы виджетов Row и Column занимали максимально возможную площадь.
Table - Табличный виджет.
Flow - Виджет на основе потока.
Stack - Виджет на основе стека.
Приложение Advanced Layout
В этом разделе давайте узнаем, как создать сложный пользовательский интерфейс списка продуктов с индивидуальным дизайном с использованием как одного, так и нескольких дочерних виджетов макета.
Для этого следуйте приведенной ниже последовательности -
Создайте новое приложение Flutter в студии Android, product_layout_app .
Замените код main.dart следующим кодом -
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
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: 'Product layout demo home page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(this.title),),
body: Center(child: Text( 'Hello World', )),
);
}
}
Here,
Мы создали виджет MyHomePage , расширив StatelessWidget вместо StatefulWidget по умолчанию, а затем удалили соответствующий код.
Теперь создайте новый виджет ProductBox в соответствии с указанным дизайном, как показано ниже -
Код для ProductBox следующий.
class ProductBox extends StatelessWidget {
ProductBox({Key key, this.name, this.description, this.price, this.image})
: super(key: key);
final String name;
final String description;
final int price;
final String image;
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2), height: 120, child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
Image.asset("assets/appimages/" +image), Expanded(
child: Container(
padding: EdgeInsets.all(5), child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(this.name, style: TextStyle(fontWeight:
FontWeight.bold)), Text(this.description),
Text("Price: " + this.price.toString()),
],
)
)
)
]
)
)
);
}
}
Пожалуйста, обратите внимание на следующее в коде -
ProductBox использовал четыре аргумента, как указано ниже -
name - Название продукта
description - Описание продукта
price - Цена товара
image - Изображение товара
ProductBox использует семь встроенных виджетов, как указано ниже -
- Container
- Expanded
- Row
- Column
- Card
- Text
- Image
ProductBox разработан с использованием вышеупомянутого виджета. Расположение или иерархия виджета указана на схеме, показанной ниже -
Теперь поместите фиктивное изображение (см. Ниже) для информации о продукте в папку ресурсов приложения и настройте папку ресурсов в файле pubspec.yaml, как показано ниже -
assets:
- assets/appimages/floppy.png
- assets/appimages/iphone.png
- assets/appimages/laptop.png
- assets/appimages/pendrive.png
- assets/appimages/pixel.png
- assets/appimages/tablet.png
iPhone.png
Pixel.png
Ноутбук.png
Tablet.png
Pendrive.png
Floppy.png
Наконец, используйте ProductBox виджет в MyHomePage виджет , как указано ниже -
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title:Text("Product Listing")),
body: ListView(
shrinkWrap: true, padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0),
children: <Widget> [
ProductBox(
name: "iPhone",
description: "iPhone is the stylist phone ever",
price: 1000,
image: "iphone.png"
),
ProductBox(
name: "Pixel",
description: "Pixel is the most featureful phone ever",
price: 800,
image: "pixel.png"
),
ProductBox(
name: "Laptop",
description: "Laptop is most productive development tool",
price: 2000,
image: "laptop.png"
),
ProductBox(
name: "Tablet",
description: "Tablet is the most useful device ever for meeting",
price: 1500,
image: "tablet.png"
),
ProductBox(
name: "Pendrive",
description: "Pendrive is useful storage medium",
price: 100,
image: "pendrive.png"
),
ProductBox(
name: "Floppy Drive",
description: "Floppy drive is useful rescue storage medium",
price: 20,
image: "floppy.png"
),
],
)
);
}
}
Здесь мы использовали ProductBox как дочерние элементы виджета ListView .
Полный код (main.dart) приложения макета продукта (product_layout_app) выглядит следующим образом:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
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: 'Product layout demo home page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Product Listing")),
body: ListView(
shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0),
children: <Widget>[
ProductBox(
name: "iPhone",
description: "iPhone is the stylist phone ever",
price: 1000,
image: "iphone.png"
),
ProductBox(
name: "Pixel",
description: "Pixel is the most featureful phone ever",
price: 800,
image: "pixel.png"
),
ProductBox(
name: "Laptop",
description: "Laptop is most productive development tool",
price: 2000,
image: "laptop.png"
),
ProductBox(
name: "Tablet",
description: "Tablet is the most useful device ever for meeting",
price: 1500,
image: "tablet.png"
),
ProductBox(
name: "Pendrive",
description: "Pendrive is useful storage medium",
price: 100,
image: "pendrive.png"
),
ProductBox(
name: "Floppy Drive",
description: "Floppy drive is useful rescue storage medium",
price: 20,
image: "floppy.png"
),
],
)
);
}
}
class ProductBox extends StatelessWidget {
ProductBox({Key key, this.name, this.description, this.price, this.image}) :
super(key: key);
final String name;
final String description;
final int price;
final String image;
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2),
height: 120,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Image.asset("assets/appimages/" + image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
this.name, style: TextStyle(
fontWeight: FontWeight.bold
)
),
Text(this.description), Text(
"Price: " + this.price.toString()
),
],
)
)
)
]
)
)
);
}
}
Окончательный вывод приложения выглядит следующим образом -
Жесты - это, прежде всего, способ взаимодействия пользователя с мобильным приложением (или любым сенсорным устройством). Жесты обычно определяются как любое физическое действие / движение пользователя с намерением активировать определенный элемент управления мобильным устройством. Жесты так же просты, как касание экрана мобильного устройства, для более сложных действий, используемых в игровых приложениях.
Здесь упоминаются некоторые из широко используемых жестов -
Tap - Коснитесь поверхности устройства кончиком пальца на короткое время, а затем отпустите кончик пальца.
Double Tap - Двойное нажатие за короткое время.
Drag - Касание поверхности устройства кончиком пальца, затем устойчивое движение кончиком пальца и, наконец, отпускание кончика пальца.
Flick - Подобно перетаскиванию, но делает это быстрее.
Pinch - Зажмите поверхность устройства двумя пальцами.
Spread/Zoom - Противоположность защемлению.
Panning - Касаясь поверхности устройства кончиком пальца и перемещая его в любом направлении, не отпуская кончика пальца.
Flutter обеспечивает отличную поддержку всех типов жестов через свой эксклюзивный виджет, GestureDetector. GestureDetector - это невизуальный виджет, который в основном используется для обнаружения жестов пользователя. Чтобы идентифицировать жест, нацеленный на виджет, виджет можно поместить внутри виджета GestureDetector. GestureDetector фиксирует жест и отправляет несколько событий на основе этого жеста.
Некоторые жесты и соответствующие события приведены ниже -
- Tap
- onTapDown
- onTapUp
- onTap
- onTapCancel
- Двойное нажатие
- onDoubleTap
- Долгое нажатие
- onLongPress
- Вертикальное сопротивление
- onVerticalDragStart
- onVerticalDragUpdate
- onVerticalDragEnd
- Горизонтальное сопротивление
- onHorizontalDragStart
- onHorizontalDragUpdate
- onHorizontalDragEnd
- Pan
- onPanStart
- onPanUpdate
- onPanEnd
Теперь давайте изменим приложение hello world, включив в него функцию обнаружения жестов, и попытаемся понять концепцию.
Измените содержимое тела виджета MyHomePage, как показано ниже -
body: Center(
child: GestureDetector(
onTap: () {
_showDialog(context);
},
child: Text( 'Hello World', )
)
),
Обратите внимание, что здесь мы разместили виджет GestureDetector над виджетом Text в иерархии виджетов, зафиксировали событие onTap и, наконец, показали диалоговое окно.
Реализуйте функцию * _showDialog * для отображения диалогового окна, когда пользователь вкладывает сообщение hello world . Он использует общие виджеты showDialog и AlertDialog для создания нового виджета диалога. Код показан ниже -
// user defined function void _showDialog(BuildContext context) {
// flutter defined function
showDialog(
context: context, builder: (BuildContext context) {
// return object of type Dialog
return AlertDialog(
title: new Text("Message"),
content: new Text("Hello World"),
actions: <Widget>[
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
Приложение перезагрузится на устройстве с помощью функции Hot Reload. Теперь просто щелкните сообщение Hello World, и появится диалоговое окно, как показано ниже -
Теперь закройте диалоговое окно, щелкнув опцию закрытия в диалоговом окне.
Полный код (main.dart) выглядит следующим образом -
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Hello World Demo Application',
theme: ThemeData( primarySwatch: Colors.blue,),
home: MyHomePage(title: 'Home page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
// user defined function
void _showDialog(BuildContext context) {
// flutter defined function showDialog(
context: context, builder: (BuildContext context) {
// return object of type Dialog return AlertDialog(
title: new Text("Message"),
content: new Text("Hello World"),
actions: <Widget>[
new FlatButton(
child: new Text("Close"),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(this.title),),
body: Center(
child: GestureDetector(
onTap: () {
_showDialog(context);
},
child: Text( 'Hello World', )
)
),
);
}
}
Наконец, Flutter также предоставляет низкоуровневый механизм обнаружения жестов через виджет Listener . Он обнаружит все взаимодействия с пользователем, а затем отправит следующие события:
- PointerDownEvent
- PointerMoveEvent
- PointerUpEvent
- PointerCancelEvent
Flutter также предоставляет небольшой набор виджетов для выполнения конкретных, а также расширенных жестов. Виджеты перечислены ниже -
Dismissible - Поддерживает жест пролистывания, чтобы закрыть виджет.
Draggable - Поддерживает жест перетаскивания для перемещения виджета.
LongPressDraggable - Поддерживает жест перетаскивания для перемещения виджета, когда его родительский виджет также можно перетаскивать.
DragTarget- Принимает любой перетаскиваемый виджет
IgnorePointer - Скрывает виджет и его дочерние элементы от процесса обнаружения жестов.
AbsorbPointer - Останавливает сам процесс обнаружения жестов, поэтому любой перекрывающийся виджет также не может участвовать в процессе обнаружения жестов, и, следовательно, событие не возникает.
Scrollable - Поддержка прокрутки содержимого, доступного внутри виджета.
Управление состоянием в приложении - один из наиболее важных и необходимых процессов в жизненном цикле приложения.
Давайте рассмотрим простое приложение корзины покупок.
Пользователь войдет в приложение, используя свои учетные данные.
После того, как пользователь вошел в систему, приложение должно сохранять данные вошедшего в систему пользователя на всем экране.
Опять же, когда пользователь выбирает продукт и сохраняет его в корзину, информация о корзине должна сохраняться между страницами, пока пользователь не выберет корзину.
Информация о пользователе и его корзине в любом случае называется состоянием приложения в этом случае.
Управление состоянием можно разделить на две категории в зависимости от продолжительности определенного состояния в приложении.
Ephemeral- Длится несколько секунд, как текущее состояние анимации, или отдельная страница, например, текущий рейтинг продукта. Flutter поддерживает это через StatefulWidget.
app state- Последнее для всего приложения, такого как данные авторизованного пользователя, информация о корзине и т. Д., Flutter поддерживает его через scoped_model.
Навигация и маршрутизация
В любом приложении переход с одной страницы / экрана на другой определяет рабочий процесс приложения. Способ управления навигацией в приложении называется маршрутизацией. Flutter предоставляет базовый класс маршрутизации - MaterialPageRoute и два метода - Navigator.push и Navigator.pop, для определения рабочего процесса приложения.
МатериалPageRoute
MaterialPageRoute - это виджет, используемый для визуализации пользовательского интерфейса путем замены всего экрана анимацией для конкретной платформы.
MaterialPageRoute(builder: (context) => Widget())
Здесь конструктор принимает функцию для создания своего содержимого, предоставляя текущий контекст приложения.
Navigation.push
Navigation.push используется для перехода на новый экран с помощью виджета MaterialPageRoute.
Navigator.push( context, MaterialPageRoute(builder: (context) => Widget()), );
Navigation.pop
Navigation.pop используется для перехода к предыдущему экрану.
Navigator.pop(context);
Давайте создадим новое приложение, чтобы лучше понять концепцию навигации.
Создайте новое приложение Flutter в студии Android, product_nav_app
Скопируйте папку ресурсов из product_nav_app в product_state_app и добавьте ресурсы в файл pubspec.yaml.
flutter:
assets:
- assets/appimages/floppy.png
- assets/appimages/iphone.png
- assets/appimages/laptop.png
- assets/appimages/pendrive.png
- assets/appimages/pixel.png
- assets/appimages/tablet.png
Замените код запуска по умолчанию (main.dart) на наш код запуска.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
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: 'Product state demo home page'
),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: Center(
child: Text('Hello World',)
),
);
}
}
Давайте создадим класс Product для организации информации о продукте.
class Product {
final String name;
final String description;
final int price;
final String image;
Product(this.name, this.description, this.price, this.image);
}
Давайте напишем метод getProducts в классе Product для создания наших фиктивных записей продуктов.
static List<Product> getProducts() {
List<Product> items = <Product>[];
items.add(
Product(
"Pixel",
"Pixel is the most feature-full phone ever", 800,
"pixel.png"
)
);
items.add(
Product(
"Laptop",
"Laptop is most productive development tool",
2000, "
laptop.png"
)
);
items.add(
Product(
"Tablet",
"Tablet is the most useful device ever for meeting",
1500,
"tablet.png"
)
);
items.add(
Product(
"Pendrive",
"Pendrive is useful storage medium",
100,
"pendrive.png"
)
);
items.add(
Product(
"Floppy Drive",
"Floppy drive is useful rescue storage medium",
20,
"floppy.png"
)
);
return items;
}
import product.dart in main.dart
import 'Product.dart';
Давайте добавим наш новый виджет RatingBox.
class RatingBox extends StatefulWidget {
@override
_RatingBoxState createState() =>_RatingBoxState();
}
class _RatingBoxState extends State<RatingBox> {
int _rating = 0;
void _setRatingAsOne() {
setState(() {
_rating = 1;
});
}
void _setRatingAsTwo() {
setState(() {
_rating = 2;
});
}
void _setRatingAsThree() {
setState(() {
_rating = 3;
});
}
Widget build(BuildContext context) {
double _size = 20;
print(_rating);
return Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (
_rating >= 1?
Icon(
Icons.star,
size: _size,
)
: Icon(
Icons.star_border,
size: _size,
)
),
color: Colors.red[500],
onPressed: _setRatingAsOne,
iconSize: _size,
),
),
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (
_rating >= 2?
Icon(
Icons.star,
size: _size,
)
: Icon(
Icons.star_border,
size: _size,
)
),
color: Colors.red[500],
onPressed: _setRatingAsTwo,
iconSize: _size,
),
),
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (
_rating >= 3 ?
Icon(
Icons.star,
size: _size,
)
: Icon(
Icons.star_border,
size: _size,
)
),
color: Colors.red[500],
onPressed: _setRatingAsThree,
iconSize: _size,
),
),
],
);
}
}
Давайте изменим наш виджет ProductBox для работы с нашим новым классом Product.
class ProductBox extends StatelessWidget {
ProductBox({Key key, this.item}) : super(key: key);
final Product item;
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2),
height: 140,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Image.asset("assets/appimages/" + this.item.image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(this.item.name,
style: TextStyle(fontWeight: FontWeight.bold)),
Text(this.item.description),
Text("Price: " + this.item.price.toString()),
RatingBox(),
],
)
)
)
]
),
)
);
}
}
Давайте перепишем наш виджет MyHomePage, чтобы он работал с моделью продукта и отображал все продукты с помощью ListView.
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
final items = Product.getProducts();
@override
Widget build(BuildContext context) {
return Scaffold( appBar: AppBar(title: Text("Product Navigation")),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return GestureDetector(
child: ProductBox(item: items[index]),
onTap: () {
Navigator.push(
context, MaterialPageRoute(
builder: (context) => ProductPage(item: items[index]),
),
);
},
);
},
));
}
}
Здесь мы использовали MaterialPageRoute для перехода на страницу сведений о продукте.
Теперь давайте добавим ProductPage, чтобы показать подробную информацию о продукте.
class ProductPage extends StatelessWidget {
ProductPage({Key key, this.item}) : super(key: key);
final Product item;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.item.name),
),
body: Center(
child: Container(
padding: EdgeInsets.all(0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image.asset("assets/appimages/" + this.item.image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
this.item.name, style: TextStyle(
fontWeight: FontWeight.bold
)
),
Text(this.item.description),
Text("Price: " + this.item.price.toString()),
RatingBox(),
],
)
)
)
]
),
),
),
);
}
}
Полный код приложения выглядит следующим образом -
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class Product {
final String name;
final String description;
final int price;
final String image;
Product(this.name, this.description, this.price, this.image);
static List<Product> getProducts() {
List<Product> items = <Product>[];
items.add(
Product(
"Pixel",
"Pixel is the most featureful phone ever",
800,
"pixel.png"
)
);
items.add(
Product(
"Laptop",
"Laptop is most productive development tool",
2000,
"laptop.png"
)
);
items.add(
Product(
"Tablet",
"Tablet is the most useful device ever for meeting",
1500,
"tablet.png"
)
);
items.add(
Product(
"Pendrive",
"iPhone is the stylist phone ever",
100,
"pendrive.png"
)
);
items.add(
Product(
"Floppy Drive",
"iPhone is the stylist phone ever",
20,
"floppy.png"
)
);
items.add(
Product(
"iPhone",
"iPhone is the stylist phone ever",
1000,
"iphone.png"
)
);
return items;
}
}
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: 'Product Navigation demo home page'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
final items = Product.getProducts();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Product Navigation")),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return GestureDetector(
child: ProductBox(item: items[index]),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductPage(item: items[index]),
),
);
},
);
},
)
);
}
}
class ProductPage extends StatelessWidget {
ProductPage({Key key, this.item}) : super(key: key);
final Product item;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.item.name),
),
body: Center(
child: Container(
padding: EdgeInsets.all(0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image.asset("assets/appimages/" + this.item.image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(this.item.name, style: TextStyle(fontWeight: FontWeight.bold)),
Text(this.item.description),
Text("Price: " + this.item.price.toString()),
RatingBox(),
],
)
)
)
]
),
),
),
);
}
}
class RatingBox extends StatefulWidget {
@override
_RatingBoxState createState() => _RatingBoxState();
}
class _RatingBoxState extends State<RatingBox> {
int _rating = 0;
void _setRatingAsOne() {
setState(() {
_rating = 1;
});
}
void _setRatingAsTwo() {
setState(() {
_rating = 2;
});
}
void _setRatingAsThree() {
setState(() {
_rating = 3;
});
}
Widget build(BuildContext context) {
double _size = 20;
print(_rating);
return Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (
_rating >= 1 ? Icon(
Icons.star,
size: _size,
)
: Icon(
Icons.star_border,
size: _size,
)
),
color: Colors.red[500],
onPressed: _setRatingAsOne,
iconSize: _size,
),
),
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (
_rating >= 2 ?
Icon(
Icons.star,
size: _size,
)
: Icon(
Icons.star_border,
size: _size,
)
),
color: Colors.red[500],
onPressed: _setRatingAsTwo,
iconSize: _size,
),
),
Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (
_rating >= 3 ?
Icon(
Icons.star,
size: _size,
)
: Icon(
Icons.star_border,
size: _size,
)
),
color: Colors.red[500],
onPressed: _setRatingAsThree,
iconSize: _size,
),
),
],
);
}
}
class ProductBox extends StatelessWidget {
ProductBox({Key key, this.item}) : super(key: key);
final Product item;
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2),
height: 140,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Image.asset("assets/appimages/" + this.item.image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(this.item.name, style: TextStyle(fontWeight: FontWeight.bold)), Text(this.item.description),
Text("Price: " + this.item.price.toString()),
RatingBox(),
],
)
)
)
]
),
)
);
}
}
Запустите приложение и щелкните любой элемент продукта. Будет показана соответствующая страница с подробностями. Мы можем перейти на домашнюю страницу, нажав кнопку «Назад». Страница со списком продуктов и страница с подробностями о продукте в приложении показаны следующим образом:
Анимация - сложная процедура в любом мобильном приложении. Несмотря на свою сложность, Animation выводит пользовательский опыт на новый уровень и обеспечивает богатое взаимодействие с пользователем. Благодаря своему разнообразию анимация становится неотъемлемой частью современного мобильного приложения. Фреймворк Flutter признает важность анимации и предоставляет простую и интуитивно понятную структуру для разработки всех типов анимации.
Введение
Анимация - это процесс показа серии изображений / картинок в определенном порядке в течение определенной продолжительности для создания иллюзии движения. Наиболее важные аспекты анимации следующие:
У анимации есть два различных значения: начальное значение и конечное значение. Анимация начинается с начального значения, проходит ряд промежуточных значений и, наконец, заканчивается конечными значениями. Например, для анимации исчезновения виджета начальным значением будет полная непрозрачность, а конечным значением будет нулевая непрозрачность.
Промежуточные значения могут быть линейными или нелинейными (кривая) по своей природе, и их можно настраивать. Поймите, что анимация работает так, как настроена. Каждая конфигурация обеспечивает различное ощущение анимации. Например, затухание виджета будет линейным по своей природе, тогда как отскок мяча будет нелинейным по своей природе.
Продолжительность процесса анимации влияет на скорость (медленность или быстрота) анимации.
Возможность управления процессом анимации, например запуском анимации, остановкой анимации, повторением анимации заданное количество раз, обращением процесса анимации и т. Д.
Во Flutter система анимации не делает реальной анимации. Вместо этого он предоставляет только значения, необходимые для каждого кадра для рендеринга изображений.
Классы на основе анимации
Система анимации Flutter основана на объектах Animation. Основные классы анимации и их использование следующие:
Анимация
Создает интерполированные значения между двумя числами в течение определенного периода времени. Наиболее распространенные классы анимации -
Animation<double> - интерполировать значения между двумя десятичными числами
Animation<Color> - интерполировать цвета между двумя цветами
Animation<Size> - интерполировать размеры между двумя размерами
AnimationController- Специальный объект Animation для управления самой анимацией. Он генерирует новые значения всякий раз, когда приложение готово к новому кадру. Он поддерживает линейную анимацию, а значение начинается от 0,0 до 1,0.
controller = AnimationController(duration: const Duration(seconds: 2), vsync: this);
Здесь контроллер управляет анимацией, а параметр продолжительности управляет продолжительностью процесса анимации. vsync - это специальный параметр, используемый для оптимизации ресурсов, используемых в анимации.
ИзогнутыйАнимация
Подобен AnimationController, но поддерживает нелинейную анимацию. CurvedAnimation можно использовать вместе с объектом Animation, как показано ниже -
controller = AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = CurvedAnimation(parent: controller, curve: Curves.easeIn)
Подросток <T>
Производный от Animatable <T> и используемый для генерации чисел между любыми двумя числами, кроме 0 и 1. Его можно использовать вместе с объектом Animation, используя метод animate и передавая фактический объект Animation.
AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this); Animation<int> customTween = IntTween(
begin: 0, end: 255).animate(controller);
Tween также можно использовать вместе с CurvedAnimation, как показано ниже -
AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
final Animation curve = CurvedAnimation(parent: controller, curve: Curves.easeOut);
Animation<int> customTween = IntTween(begin: 0, end: 255).animate(curve);
Здесь контроллер - это фактический контроллер анимации. Кривая обеспечивает тип нелинейности, а customTween предоставляет настраиваемый диапазон от 0 до 255.
Рабочий процесс анимации флаттера
Рабочий процесс анимации выглядит следующим образом -
Определите и запустите контроллер анимации в initState StatefulWidget.
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween<double>(begin: 0, end: 300).animate(controller);
controller.forward();
Добавьте прослушиватель на основе анимации, addListener, чтобы изменить состояние виджета.
animation = Tween<double>(begin: 0, end: 300).animate(controller) ..addListener(() {
setState(() {
// The state that has changed here is the animation object’s value.
});
});
Чтобы пропустить этот процесс, можно использовать встроенные виджеты, AnimatedWidget и AnimatedBuilder. Оба виджета принимают объект Animation и получают текущие значения, необходимые для анимации.
Получите значения анимации в процессе сборки виджета, а затем примените их для ширины, высоты или любого соответствующего свойства вместо исходного значения.
child: Container(
height: animation.value,
width: animation.value,
child: <Widget>,
)
Рабочее приложение
Давайте напишем простое приложение на основе анимации, чтобы понять концепцию анимации во фреймворке Flutter.
Создайте новое приложение Flutter в студии Android, product_animation_app.
Скопируйте папку ресурсов из product_nav_app в product_animation_app и добавьте ресурсы в файл pubspec.yaml.
flutter:
assets:
- assets/appimages/floppy.png
- assets/appimages/iphone.png
- assets/appimages/laptop.png
- assets/appimages/pendrive.png
- assets/appimages/pixel.png
- assets/appimages/tablet.png
Удалите код запуска по умолчанию (main.dart).
Добавьте импорт и базовую основную функцию.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
Создайте виджет MyApp, производный от StatefulWidgtet.
class MyApp extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
Создайте виджет _MyAppState и реализуйте initState и удалите в дополнение к методу сборки по умолчанию.
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(seconds: 10), vsync: this
);
animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
controller.forward();
}
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
controller.forward();
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue,),
home: MyHomePage(title: 'Product layout demo home page', animation: animation,)
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
Вот,
В методе initState мы создали объект контроллера анимации (контроллер), объект анимации (анимацию) и запустили анимацию с помощью controller.forward.
В методе dispose мы удалили объект контроллера анимации (контроллер).
В методе сборки отправьте анимацию в виджет MyHomePage через конструктор. Теперь виджет MyHomePage может использовать объект анимации для анимации своего содержимого.
Теперь добавьте виджет ProductBox
class ProductBox extends StatelessWidget {
ProductBox({Key key, this.name, this.description, this.price, this.image})
: super(key: key);
final String name;
final String description;
final int price;
final String image;
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2),
height: 140,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Image.asset("assets/appimages/" + image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(this.name, style:
TextStyle(fontWeight: FontWeight.bold)),
Text(this.description),
Text("Price: " + this.price.toString()),
],
)
)
)
]
)
)
);
}
}
Создайте новый виджет MyAnimatedWidget для простой анимации затухания с использованием непрозрачности.
class MyAnimatedWidget extends StatelessWidget {
MyAnimatedWidget({this.child, this.animation});
final Widget child;
final Animation<double> animation;
Widget build(BuildContext context) => Center(
child: AnimatedBuilder(
animation: animation,
builder: (context, child) => Container(
child: Opacity(opacity: animation.value, child: child),
),
child: child),
);
}
Здесь мы использовали AniatedBuilder для создания нашей анимации. AnimatedBuilder - это виджет, который создает свой контент, одновременно выполняя анимацию. Он принимает объект анимации для получения текущего значения анимации. Мы использовали значение анимации animation.value, чтобы установить непрозрачность дочернего виджета. Фактически, виджет будет анимировать дочерний виджет, используя концепцию непрозрачности.
Наконец, создайте виджет MyHomePage и используйте объект анимации для анимации любого его содержимого.
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title, this.animation}) : super(key: key);
final String title;
final Animation<double>
animation;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Product Listing")),body: ListView(
shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0),
children: <Widget>[
FadeTransition(
child: ProductBox(
name: "iPhone",
description: "iPhone is the stylist phone ever",
price: 1000,
image: "iphone.png"
), opacity: animation
),
MyAnimatedWidget(child: ProductBox(
name: "Pixel",
description: "Pixel is the most featureful phone ever",
price: 800,
image: "pixel.png"
), animation: animation),
ProductBox(
name: "Laptop",
description: "Laptop is most productive development tool",
price: 2000,
image: "laptop.png"
),
ProductBox(
name: "Tablet",
description: "Tablet is the most useful device ever for meeting",
price: 1500,
image: "tablet.png"
),
ProductBox(
name: "Pendrive",
description: "Pendrive is useful storage medium",
price: 100,
image: "pendrive.png"
),
ProductBox(
name: "Floppy Drive",
description: "Floppy drive is useful rescue storage medium",
price: 20,
image: "floppy.png"
),
],
)
);
}
}
Здесь мы использовали FadeAnimation и MyAnimationWidget для анимации первых двух элементов в списке. FadeAnimation - это встроенный класс анимации, который мы использовали для анимации его дочернего элемента с использованием концепции непрозрачности.
Полный код выглядит следующим образом -
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(seconds: 10), vsync: this);
animation = Tween<double>(begin: 0.0, end: 1.0).animate(controller);
controller.forward();
}
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
controller.forward();
return MaterialApp(
title: 'Flutter Demo', theme: ThemeData(primarySwatch: Colors.blue,),
home: MyHomePage(title: 'Product layout demo home page', animation: animation,)
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title, this.animation}): super(key: key);
final String title;
final Animation<double> animation;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Product Listing")),
body: ListView(
shrinkWrap: true,
padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0),
children: <Widget>[
FadeTransition(
child: ProductBox(
name: "iPhone",
description: "iPhone is the stylist phone ever",
price: 1000,
image: "iphone.png"
),
opacity: animation
),
MyAnimatedWidget(
child: ProductBox(
name: "Pixel",
description: "Pixel is the most featureful phone ever",
price: 800,
image: "pixel.png"
),
animation: animation
),
ProductBox(
name: "Laptop",
description: "Laptop is most productive development tool",
price: 2000,
image: "laptop.png"
),
ProductBox(
name: "Tablet",
description: "Tablet is the most useful device ever for meeting",
price: 1500,
image: "tablet.png"
),
ProductBox(
name: "Pendrive",
description: "Pendrive is useful storage medium",
price: 100,
image: "pendrive.png"
),
ProductBox(
name: "Floppy Drive",
description: "Floppy drive is useful rescue storage medium",
price: 20,
image: "floppy.png"
),
],
)
);
}
}
class ProductBox extends StatelessWidget {
ProductBox({Key key, this.name, this.description, this.price, this.image}) :
super(key: key);
final String name;
final String description;
final int price;
final String image;
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(2),
height: 140,
child: Card(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Image.asset("assets/appimages/" + image),
Expanded(
child: Container(
padding: EdgeInsets.all(5),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
this.name, style: TextStyle(
fontWeight: FontWeight.bold
)
),
Text(this.description), Text(
"Price: " + this.price.toString()
),
],
)
)
)
]
)
)
);
}
}
class MyAnimatedWidget extends StatelessWidget {
MyAnimatedWidget({this.child, this.animation});
final Widget child;
final Animation<double> animation;
Widget build(BuildContext context) => Center(
child: AnimatedBuilder(
animation: animation,
builder: (context, child) => Container(
child: Opacity(opacity: animation.value, child: child),
),
child: child
),
);
}
Скомпилируйте и запустите приложение, чтобы увидеть результаты. Начальная и окончательная версии приложения следующие:
Flutter предоставляет общую структуру для доступа к конкретной функции платформы. Это позволяет разработчику расширить функциональность фреймворка Flutter с помощью кода, специфичного для платформы. Функциональные возможности платформы, такие как камера, уровень заряда батареи, браузер и т. Д., Могут быть легко доступны через фреймворк.
Общая идея доступа к коду, специфичному для платформы, заключается в простом протоколе обмена сообщениями. Код Flutter, Клиент и код платформы и Хост привязываются к общему каналу сообщений. Клиент отправляет сообщение Хосту через канал сообщений. Хост прослушивает канал сообщений, принимает сообщение и выполняет необходимые функции и, наконец, возвращает результат клиенту через канал сообщений.
Архитектура кода для конкретной платформы показана на блок-схеме, приведенной ниже -
Протокол обмена сообщениями использует стандартный кодек сообщений (класс StandardMessageCodec), который поддерживает двоичную сериализацию JSON-подобных значений, таких как числа, строки, логические значения и т. Д. Сериализация и десериализация работают прозрачно между клиентом и хостом.
Давайте напишем простое приложение для открытия браузера с помощью Android SDK и поймем, как
Создайте новое приложение Flutter в студии Android, flutter_browser_app
Замените код main.dart приведенным ниже кодом -
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@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 StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: Center(
child: RaisedButton(
child: Text('Open Browser'),
onPressed: null,
),
),
);
}
}
Здесь мы создали новую кнопку, чтобы открыть браузер, и установили для его метода onPressed значение null.
Теперь импортируйте следующие пакеты -
import 'dart:async';
import 'package:flutter/services.dart';
Здесь services.dart включает функциональные возможности для вызова кода платформы.
Создайте новый канал сообщений в виджете MyHomePage.
static const platform = const
MethodChannel('flutterapp.tutorialspoint.com/browser');
Напишите метод _openBrowser для вызова специфичного для платформы метода openBrowser через канал сообщений.
Future<void> _openBrowser() async {
try {
final int result = await platform.invokeMethod(
'openBrowser', <String, String>{
'url': "https://flutter.dev"
}
);
}
on PlatformException catch (e) {
// Unable to open the browser
print(e);
}
}
Здесь мы использовали platform.invokeMethod для вызова openBrowser (объяснено в следующих шагах). openBrowser имеет аргумент url для открытия определенного url.
Измените значение свойства onPressed объекта RaisedButton с нуля на _openBrowser.
onPressed: _openBrowser,
Откройте MainActivity.java (внутри папки android) и импортируйте необходимую библиотеку -
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.GeneratedPluginRegistrant;
Напишите метод openBrowser для открытия браузера
private void openBrowser(MethodCall call, Result result, String url) {
Activity activity = this;
if (activity == null) {
result.error("ACTIVITY_NOT_AVAILABLE",
"Browser cannot be opened without foreground
activity", null);
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
result.success((Object) true);
}
Теперь установите имя канала в классе MainActivity -
private static final String CHANNEL = "flutterapp.tutorialspoint.com/browser";
Напишите специальный код для Android, чтобы настроить обработку сообщений в методе onCreate -
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
String url = call.argument("url");
if (call.method.equals("openBrowser")) {
openBrowser(call, result, url);
} else {
result.notImplemented();
}
}
});
Здесь мы создали канал сообщения с использованием класса MethodChannel и использовали класс MethodCallHandler для обработки сообщения. onMethodCall - это фактический метод, отвечающий за вызов правильного кода платформы путем проверки сообщения. Метод onMethodCall извлекает URL-адрес из сообщения и затем вызывает openBrowser только тогда, когда вызов метода - openBrowser. В противном случае он возвращает метод notImplemented.
Полный исходный код приложения выглядит следующим образом -
main.dart
MainActivity.java
package com.tutorialspoint.flutterapp.flutter_browser_app;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugins.GeneratedPluginRegistrant;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "flutterapp.tutorialspoint.com/browser";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
String url = call.argument("url");
if (call.method.equals("openBrowser")) {
openBrowser(call, result, url);
} else {
result.notImplemented();
}
}
}
);
}
private void openBrowser(MethodCall call, Result result, String url) {
Activity activity = this; if (activity == null) {
result.error(
"ACTIVITY_NOT_AVAILABLE", "Browser cannot be opened without foreground activity", null
);
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
result.success((Object) true);
}
}
main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@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 StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
static const platform = const MethodChannel('flutterapp.tutorialspoint.com/browser');
Future<void> _openBrowser() async {
try {
final int result = await platform.invokeMethod('openBrowser', <String, String>{
'url': "https://flutter.dev"
});
}
on PlatformException catch (e) {
// Unable to open the browser print(e);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: Center(
child: RaisedButton(
child: Text('Open Browser'),
onPressed: _openBrowser,
),
),
);
}
}
Запустите приложение и нажмите кнопку «Открыть браузер», и вы увидите, что браузер запущен. Приложение "Браузер" - главная страница, как показано на скриншоте здесь -
Доступ к коду, специфичному для iOS, аналогичен доступу на платформе Android, за исключением того, что он использует специальные языки iOS - Objective-C или Swift и iOS SDK. В остальном концепция такая же, как у платформы Android.
Напишем то же приложение, что и в предыдущей главе, и для платформы iOS.
Давайте создадим новое приложение в Android Studio (macOS), flutter_browser_ios_app
Выполните шаги 2–6, как в предыдущей главе.
Запустите XCode и нажмите File → Open
Выберите проект xcode в каталоге ios нашего проекта flutter.
Откройте AppDelegate.m под Runner → Runner path. Он содержит следующий код -
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// [GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end
Мы добавили метод openBrowser для открытия браузера с указанным URL. Он принимает единственный аргумент, url.
- (void)openBrowser:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
UIApplication *application = [UIApplication sharedApplication];
[application openURL:url];
}
В методе didFinishLaunchingWithOptions найдите контроллер и установите его в переменной контроллера.
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
В методе didFinishLaunchingWithOptions установите канал браузера как flutterapp.tutorialspoint.com/browse -
FlutterMethodChannel* browserChannel = [
FlutterMethodChannel methodChannelWithName:
@"flutterapp.tutorialspoint.com/browser" binaryMessenger:controller];
Создайте переменную weakSelf и установите текущий класс -
__weak typeof(self) weakSelf = self;
Теперь реализуйте setMethodCallHandler. Вызовите openBrowser, сопоставив call.method. Получите URL-адрес, вызвав call.arguments, и передайте его при вызове openBrowser.
[browserChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
if ([@"openBrowser" isEqualToString:call.method]) {
NSString *url = call.arguments[@"url"];
[weakSelf openBrowser:url];
} else { result(FlutterMethodNotImplemented); }
}];
Полный код выглядит следующим образом -
#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// custom code starts
FlutterViewController* controller = (FlutterViewController*)self.window.rootViewController;
FlutterMethodChannel* browserChannel = [
FlutterMethodChannel methodChannelWithName:
@"flutterapp.tutorialspoint.com /browser" binaryMessenger:controller];
__weak typeof(self) weakSelf = self;
[browserChannel setMethodCallHandler:^(
FlutterMethodCall* call, FlutterResult result) {
if ([@"openBrowser" isEqualToString:call.method]) {
NSString *url = call.arguments[@"url"];
[weakSelf openBrowser:url];
} else { result(FlutterMethodNotImplemented); }
}];
// custom code ends
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)openBrowser:(NSString *)urlString {
NSURL *url = [NSURL URLWithString:urlString];
UIApplication *application = [UIApplication sharedApplication];
[application openURL:url];
}
@end
Открыть настройку проекта.
Идти к Capabilities и включить Background Modes.
Добавить *Background fetch и Remote Notification**.
Теперь запустите приложение. Он работает аналогично версии для Android, но вместо Chrome будет открыт браузер Safari.
Способ организации и совместного использования набора функций в Dart - это Package. Пакет Dart - это просто общие библиотеки или модули. В общем, Dart Package такой же, как и у Dart Application, за исключением того, что у Dart Package нет точки входа в приложение, main.
Общая структура пакета (рассмотрим демонстрационный пакет my_demo_package) выглядит следующим образом:
lib/src/* - Частные файлы кода Dart.
lib/my_demo_package.dart- Основной файл кода Dart. Его можно импортировать в приложение как -
import 'package:my_demo_package/my_demo_package.dart'
Другой файл частного кода может быть экспортирован в основной файл кода (my_demo_package.dart), если необходимо, как показано ниже -
export src/my_private_code.dart
lib/*- Любое количество файлов кода Dart, расположенных в любой пользовательской структуре папок. К коду можно получить доступ как,
import 'package:my_demo_package/custom_folder/custom_file.dart'
pubspec.yaml - Спецификация проекта, такая же, как у приложения,
Все файлы кода Dart в пакете - это просто классы Dart, и для кода Dart не требуется никаких специальных требований для включения его в пакет.
Типы пакетов
Поскольку пакеты Dart в основном представляют собой небольшой набор схожих функций, их можно разделить на категории в зависимости от их функциональности.
Пакет дротиков
Общий код Dart, который можно использовать как в веб-среде, так и в мобильной среде. Например, english_words - один из таких пакетов, который содержит около 5000 слов и имеет базовые служебные функции, такие как существительные (список существительных на английском языке), слоги (укажите количество слогов в слове.
Пакет Flutter
Общий код Dart, который зависит от фреймворка Flutter и может использоваться только в мобильной среде. Например, fluro - это настраиваемый маршрутизатор для флаттера. Это зависит от фреймворка Flutter.
Плагин Flutter
Общий код Dart, который зависит от фреймворка Flutter, а также от базового кода платформы (Android SDK или iOS SDK). Например, камера - это плагин для взаимодействия с камерой устройства. Доступ к камере зависит от платформы Flutter, а также от базовой платформы.
Использование пакета Dart
Пакеты Dart размещаются и публикуются на реальном сервере, https://pub.dartlang.org.Кроме того, Flutter предоставляет простой инструмент pub для управления пакетами Dart в приложении. Шаги, необходимые для использования в качестве пакета, следующие:
Включите имя пакета и необходимую версию в pubspec.yaml, как показано ниже -
dependencies: english_words: ^3.1.5
Номер последней версии можно узнать, проверив онлайн-сервер.
Установите пакет в приложение, используя следующую команду -
flutter packages get
При разработке в студии Android Android Studio обнаруживает любые изменения в pubspec.yaml и отображает предупреждение о пакете Android Studio для разработчика, как показано ниже:
Пакеты Dart можно установить или обновить в Android Studio с помощью параметров меню.
Импортируйте необходимый файл с помощью команды, показанной ниже, и приступайте к работе -
import 'package:english_words/english_words.dart';
Используйте любой метод, доступный в пакете,
nouns.take(50).forEach(print);
Здесь мы использовали функцию существительных, чтобы получить и распечатать первые 50 слов.
Разработка пакета плагина Flutter
Разработка плагина Flutter аналогична разработке приложения Dart или пакета Dart. Единственное исключение состоит в том, что плагин будет использовать системный API (Android или iOS) для получения необходимых функций, специфичных для платформы.
Поскольку мы уже узнали, как получить доступ к коду платформы в предыдущих главах, давайте разработаем простой плагин my_browser, чтобы понять процесс разработки плагина. Функциональность плагина my_browser заключается в том, чтобы позволить приложению открывать данный веб-сайт в браузере конкретной платформы.
Запустите Android Studio.
Нажмите File → New Flutter Project и выберите опцию Flutter Plugin.
Вы можете увидеть окно выбора плагина Flutter, как показано здесь -
Введите my_browser в качестве имени проекта и нажмите Далее.
Введите имя плагина и другие данные в окне, как показано здесь -
Введите домен компании flutterplugins.tutorialspoint.com в окне, показанном ниже, и нажмите Finish. Он сгенерирует код запуска для разработки нашего нового плагина.
Откройте файл my_browser.dart и напишите метод openBrowser для вызова специфичного для платформы метода openBrowser.
Future<void> openBrowser(String urlString) async {
try {
final int result = await _channel.invokeMethod(
'openBrowser', <String, String>{ 'url': urlString }
);
}
on PlatformException catch (e) {
// Unable to open the browser print(e);
}
}
Откройте файл MyBrowserPlugin.java и импортируйте следующие классы -
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
Здесь нам нужно импортировать библиотеку, необходимую для открытия браузера с Android.
Добавьте новую частную переменную mRegistrar типа Registrar в класс MyBrowserPlugin.
private final Registrar mRegistrar;
Здесь Регистратор используется для получения контекстной информации вызывающего кода.
Добавьте конструктор для установки Регистратора в классе MyBrowserPlugin.
private MyBrowserPlugin(Registrar registrar) {
this.mRegistrar = registrar;
}
Измените registerWith, чтобы включить наш новый конструктор в класс MyBrowserPlugin.
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), "my_browser");
MyBrowserPlugin instance = new MyBrowserPlugin(registrar);
channel.setMethodCallHandler(instance);
}
Измените onMethodCall, чтобы включить метод openBrowser в класс MyBrowserPlugin.
@Override
public void onMethodCall(MethodCall call, Result result) {
String url = call.argument("url");
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
}
else if (call.method.equals("openBrowser")) {
openBrowser(call, result, url);
} else {
result.notImplemented();
}
}
Напишите специфичный для платформы метод openBrowser для доступа к браузеру в классе MyBrowserPlugin.
private void openBrowser(MethodCall call, Result result, String url) {
Activity activity = mRegistrar.activity();
if (activity == null) {
result.error("ACTIVITY_NOT_AVAILABLE",
"Browser cannot be opened without foreground activity", null);
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
result.success((Object) true);
}
Полный исходный код плагина my_browser выглядит следующим образом:
my_browser.dart
import 'dart:async';
import 'package:flutter/services.dart';
class MyBrowser {
static const MethodChannel _channel = const MethodChannel('my_browser');
static Future<String> get platformVersion async {
final String version = await _channel.invokeMethod('getPlatformVersion'); return version;
}
Future<void> openBrowser(String urlString) async {
try {
final int result = await _channel.invokeMethod(
'openBrowser', <String, String>{'url': urlString});
}
on PlatformException catch (e) {
// Unable to open the browser print(e);
}
}
}
MyBrowserPlugin.java
package com.tutorialspoint.flutterplugins.my_browser;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
/** MyBrowserPlugin */
public class MyBrowserPlugin implements MethodCallHandler {
private final Registrar mRegistrar;
private MyBrowserPlugin(Registrar registrar) {
this.mRegistrar = registrar;
}
/** Plugin registration. */
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(
registrar.messenger(), "my_browser");
MyBrowserPlugin instance = new MyBrowserPlugin(registrar);
channel.setMethodCallHandler(instance);
}
@Override
public void onMethodCall(MethodCall call, Result result) {
String url = call.argument("url");
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
}
else if (call.method.equals("openBrowser")) {
openBrowser(call, result, url);
} else {
result.notImplemented();
}
}
private void openBrowser(MethodCall call, Result result, String url) {
Activity activity = mRegistrar.activity();
if (activity == null) {
result.error("ACTIVITY_NOT_AVAILABLE",
"Browser cannot be opened without foreground activity", null);
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
result.success((Object) true);
}
}
Создайте новый проект my_browser_plugin_test, чтобы протестировать наш недавно созданный плагин.
Откройте pubspec.yaml и установите my_browser в качестве зависимости плагина.
dependencies:
flutter:
sdk: flutter
my_browser:
path: ../my_browser
Студия Android сообщит, что pubspec.yaml обновлен, как показано в предупреждении пакета Android studio, приведенном ниже -
Щелкните параметр Получить зависимости. Студия Android получит пакет из Интернета и правильно настроит его для приложения.
Open main.dart and include my_browser plugin as below −
import 'package:my_browser/my_browser.dart';
Call the openBrowser function from my_browser plugin as shown below −
onPressed: () => MyBrowser().openBrowser("https://flutter.dev"),
The complete code of the main.dart is as follows −
import 'package:flutter/material.dart';
import 'package:my_browser/my_browser.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@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 StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(this.title),
),
body: Center(
child: RaisedButton(
child: Text('Open Browser'),
onPressed: () => MyBrowser().openBrowser("https://flutter.dev"),
),
),
);
}
}
Run the application and click the Open Browser button and see that the browser is launched. You can see a Browser app - Home page as shown in the screenshot shown below −
You can see a Browser app – Browser screen as shown in the screenshot shown below −
- Add Notes
- Bookmark this page
- Report Error
- Suggestions
Save Close