Напишите свое первое приложение

Часть 1

Совет: Эта лабораторная работа проведет вас через написание вашего первого приложения Flutter на мобильном телефоне. Возможно, вы предпочтете попробовать написать ваше первое Flutter-приложение в формате Web. Обратите внимание, что если вы включили Web, то готовое приложение просто работает на всех этих устройствах!

Это руководство по созданию вашего первого приложения Flutter. Если вы знакомы с объектно-ориентированным кодом и основными понятиями программирования, такими как переменные, циклы и константы, вы можете завершить это руководство. Вам не нужен предыдущий опыт работы с Dart, мобильным или веб-программированием.

Эта лабораторная работа является 1 частью двухкомпонентной лабораторной работы. Вы можете найти часть 2 на Google Developers Codelabs (а также копия этой лабораторной работы, часть 1).

То, что вы построите в первой части.

То, что вы создадите в первой части

Вы реализуете простое приложение, которое генерирует предлагаемые имена для стартап-компании. Пользователь может выбирать и отменять выбор имен, сохраняя лучшие из них. Код лениво генерирует 10 имен за раз. В то время как пользователь прокручивает список, генерируется больше имен. Нет предела тому, как далеко пользователь может прокручивать список.

Анимированный GIF показывает, как приложение работает по завершению части 1.

То, что вы узнаете в первой части:

  • Как написать flutter-приложение, которое выглядит естественно на iOS, Android и WEB
  • Базовая структура приложения Flutter
  • Поиск и использование пакетов для расширения функциональности Использование горячей перезагрузки hot reload для более быстрого цикла разработки
  • Как реализовать Stateful-виджет
  • Как создать бесконечно подгружаемый список

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

Что вы будете использовать:

Для работы в этой лаборатории вам понадобятся две программы: Flutter SDK и редактор. Эта лабораторная работа предполагает использование Android Studio, но вы можете использовать предпочитаемый вами редактор.

Вы можете запустить эту лабораторную работу с помощью любого из следующих устройств:

  • Физическое устройство (Android или iOS), подключенное к компьютеру и переходящее в режим разработчика.
  • Симулятор iOS (требуется установка инструментов Xcode).
  • Эмулятор Android (требуется настройка в Android Studio)
  • Браузер (для отладки требуется Chrome).

Если вы хотите скомпилировать ваше приложение для работы в Интернете, вы должны включить эту функцию (которая в настоящее время находится в бета-версии). Чтобы включить веб-поддержку, воспользуйтесь следующими инструкциями:

 $ flutter channel beta
 $ flutter upgrade
 $ flutter config --enable-web

Вам нужно запустить команду конфигурации только один раз. После того, как вы включите веб-поддержку, каждое созданное вами приложение Flutter также компилируется для веб. В IDE в разделе devices или в командной строке flutter devices вы должны увидеть Chrome и веб-сервер в списке. Устройство Chrome автоматически запускает Chrome. Веб-сервер запускает сервер, на котором расположено приложение, чтобы его можно было загрузить из любого браузера. Используйте устройство Chrome во время разработки, чтобы можно было использовать DevTools и веб-сервер, когда нужно протестировать приложение в других браузерах. Дополнительные сведения см. в разделах «Создание веб-приложений с использованием Flutter» и «Напишите свое первое Flutter-приложение в Интернете«.

Шаг 1: Создайте стартовое Flutter приложение.

Создайте простое, шаблонированное приложение Flutter, воспользовавшись инструкциями в разделе «Начало работы с первым приложением Flutter«. Назовите проект startup_namer (вместо flutter_app).

Совет: Если вы не видите в IDE опцию "New Flutter Project", убедитесь, что у вас установлены плагины для Flutter и Dart.

В основном вы будете редактировать lib/main.dart, где находится код Dart.

  1. Замените содержимое lib/main.dart.
    Удалите весь код из lib/main.dart. Заменить следующим кодом, который отображает «Hello World» в центре экрана.

lib/main.dart:

// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          child: Text('Hello World'),
        ),
      ),
    );
  }
}
Совет: При вставке кода в приложение отступ может измениться. Вы можете исправить это с помощью следующих инструментов Flutter:

Android Studio и IntelliJ IDEA: Щелкните правой кнопкой мыши по коду и выберите Reformat Code with dartfmt.
VS Code: Щелкните правой кнопкой мыши и выберите Format Document.
Терминал: Запустите flutter формат .

2. Запустите приложение так, как описывает ваша IDE. Вы должны увидеть либо Android, либо iOS, либо веб-выход, в зависимости от вашего устройства.

Android
IOS
Совет: При первом запуске на физическом устройстве загрузка может занять некоторое время. После этого можно использовать hot reload для быстрого обновления. Save также выполняет hot reload, если приложение запущено. При запуске приложения непосредственно из консоли с помощью функции Flutter run, введите r для выполнения hot reload.

Наблюдения

  • В этом примере создается приложение «Material «. Material — это визуальный язык дизайна, который является стандартным на мобильных устройствах и в Интернете. Flutter предлагает богатый набор виджетов Material. Хорошей идеей является использование uses-material-design: true запись в разделе Flutter в вашем файле pubspec.yaml. Это позволит вам использовать больше возможностей Material, таких как их набор предопределенных иконок.
  • Метод main() использует обозначение стрелки (=>). Для однострочных функций или методов используйте обозначение со стрелкой.
  • Расширение StatelessWidget, что делает само приложение виджетом. В Flutter почти все является виджетами, включая выравнивание, отступы и разметку.
  • Виджет Scaffold из библиотеки «Material» предоставляет стандартную панель приложения, заголовок и свойство body, в котором хранится дерево виджетов для главного экрана. Поддерево виджетов может быть довольно сложным.
  • Основной задачей виджета является предоставление метода build(), который описывает, как отображать виджет с точки зрения других виджетов нижнего уровня.
  • Тело для данного примера состоит из виджета «Center«, содержащего дочерний виджет «Text«. Виджет «Center» выравнивает свое поддерево виджета по центру экрана.

Шаг 2: Используйте внешний пакет

На этом шаге вы начнете использовать пакет с открытым исходным кодом english_words, который содержит несколько тысяч наиболее употребляемых английских слов, а также некоторые утилитные функции.
Пакет english_words, а также многие другие пакеты с открытым исходным кодом, можно найти на сайте pub.dev.

  1. Файл pubspec.yaml управляет активами и зависимостями для приложения Flutter. В pubspec.yaml добавьте english_words (3.1.5 или выше) в список зависимостей:

{step1_base → step2_use_package}/pubspec.yaml

@@ -5,4 +5,5 @@    
  dependencies:            
    flutter:            
      sdk: flutter            
    cupertino_icons: ^0.1.2           
+   english_words: ^3.1.5

— убрать, + добавить

2. При просмотре файла pubspec.yaml в редакторе Android Studio нажмите кнопку Pub get. Это втянет пакет в ваш проект. В консоли должно появиться следующее:

flutter pub get
Running "flutter pub get" in startup_namer...
Process finished with exit code 0

Выполняя Pub get, автоматически генерируется файл pubspec.lock со списком всех пакетов, втянутых в проект, и их номерами версий.

3. В файле lib/main.dart импортируйте новый пакет:

lib/main.dart

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

По мере набора текста, Android Studio дает вам предложения по импорту библиотек. Затем она отображает строку импорта серым цветом, информируя вас о том, что импортируемая библиотека не используется (пока что).

4. Используйте пакет английских слов для генерации текста вместо использования строки «Hello World»:

{step1_base → step2_use_package}/lib/main.dart

@@ -9,6 +10,7 @@    
  class MyApp extends StatelessWidget {            
    @override            
    Widget build(BuildContext context) {            
+     final wordPair = WordPair.random();            
      return MaterialApp(            
        title: 'Welcome to Flutter',            
        home: Scaffold(            
@@ -16,7 +18,7 @@    
            title: Text('Welcome to Flutter'),            
          ),            
                      body: Center(            
-           child: Text('Hello World'),            
+           child: Text(wordPair.asPascalCase),            
         ),            
     ),            
);

— убрать, + добавить

Примечание: "регистр паскаля" (также известный как "верхний верблюжий регистр") означает, что каждое слово в строке, включая первое, начинается с заглавной буквы. Таким образом, "верхнийрегистр" становится "ВерхнимРегистром".

Если приложение работает, hot reload обновляет запущенное приложение. Каждый раз, когда Вы нажимаете на hot reload или сохраняете проект, в работающем приложении Вы должны видеть другую пару слов, выбранную случайным образом. Это связано с тем, что объединение слов генерируется внутри метода сборки, который запускается каждый раз, когда требуется отрисовка MaterialApp, или при переключении Платформы в Flutter Inspector.

Android
IOS

Проблемы?

Если ваше приложение работает некорректно, ищите опечатки. Если вы хотите попробовать некоторые из инструментов отладки Flutter’s, ознакомьтесь с набором инструментов отладки и профилирования DevTools. При необходимости используйте код по следующим ссылкам, чтобы вернуться к работе.

Шаг 3: Добавление Stateful виджета (виджета с индикацией состояния)

Stateless виджеты являются неизменными, а это значит, что их свойства не могут меняться — все значения являются окончательными.

Statefull виджеты поддерживают состояние, которое может изменяться в течение жизни виджета. Реализация stateful виджета требует как минимум два класса: 1) класс StatefulWidget, который создает экземпляр 2) класс State. Класс StatefulWidget сам по себе является неизменяемым и может быть выброшен и регенерирован, но класс State сохраняется в течение всего срока службы виджета.

На этом шаге вы добавите stateful виджет, RandomWords, который создает свой класс State, _RandomWordsState. Затем вы будете использовать RandomWords в качестве дочернего элемента существующего stateless виджета MyApp.

  1. Создайте код шаблона для stateful-виджета. В lib/main.dart, поместите курсор после всего кода, введите Return пару раз, чтобы начать со свежей строки.
    В IDE начните вводить stful. Редактор спросит, хотите ли вы создать Stateful виджет. Нажмите кнопку Return, чтобы принять. Появится код шаблона для двух классов, и курсор поместится для ввода имени вашего statefull виджета.
  2. Введите RandomWords в качестве имени вашего виджета.
    Виджет RandomWords мало что делает кроме создания своего класса State.

    После того, как вы ввели RandomWords в качестве statefull виджета, IDE автоматически обновляет сопровождающий его класс State, называя его _RandomWordsState. По умолчанию имя класса State имеет префикс с подбаром. Префиксация идентификатора с подчеркиванием обеспечивает конфиденциальность в языке Dart и рекомендуется для объектов State.

IDE также автоматически обновляет класс State для расширения State, указывая, что вы используете дженерик — общий класс State, специализирующийся на использовании RandomWords. Большая часть логики приложения находится здесь — оно поддерживает состояние для виджета RandomWords. Этот класс сохраняет список сгенерированных пар слов, который растет бесконечно по мере прокрутки пользователем, а во 2-й части этой лабораторной работы — избранные пары слов по мере добавления или удаления пользователем их из списка путем переключения значка сердца.

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

class RandomWords extends StatefulWidget {
  @override
  _RandomWordsState createState() => _RandomWordsState();
}

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}


3. Обновите метод build() в _RandomWordsState:

lib/main.dart (_RandomWordsState)

class _RandomWordsState extends State<RandomWords> {
    @override
    Widget build(BuildContext context) {
      final wordPair = WordPair.random();
      return Text(wordPair.asPascalCase);
    }
  }

После добавления state класса, IDE жалуется, что в классе отсутствует build метод. Далее будет добавлен базовый build метод, который генерирует пары слов, перемещая код генерации слов из MyApp в _RandomWordsState.

4. Удалите код генерации слова из MyApp, сделав изменения, показанные в следующем различии:

{step2_use_package → step3_stateful_widget}/lib/main.dart

@@ -10,7 +10,6 @@      
  class MyApp extends StatelessWidget {                
    @override                
    Widget build(BuildContext context) {                
-     final wordPair = WordPair.random();                
      return MaterialApp(                
        title: 'Welcome to Flutter',                
        home: Scaffold(                
@@ -18,8 +17,8 @@      
            title: Text('Welcome to Flutter'),                
          ),                
          body: Center(                
-           child: Text(wordPair.asPascalCase),                
+          child: RandomWords(),                
          ),                
        ),                
      );                
    }

— убрать, + добавить

5. Перезапустите приложение. Приложение должно вести себя как раньше, отображая пару слов каждый раз при hot reload или сохранении приложения.

Совет: Если вы видите предупреждение на hot reload, это значит, что вам может понадобиться перезагрузить приложение, подумайте о его перезапуске. Предупреждение может быть ложноположительным, но перезапуск приложения гарантирует, что ваши изменения будут отражены в пользовательском интерфейсе приложения.

Проблемы?

Если ваше приложение работает некорректно, ищите опечатки. Если вы хотите попробовать некоторые из инструментов отладки Flutter’s, ознакомьтесь с набором инструментов отладки и профилирования DevTools. При необходимости используйте код, приведенный по следующей ссылке, чтобы вернуться к работе.

Шаг 4: Создание бесконечного прокручиваемого ListView

На этом шаге вы разворачиваете _RandomWordsState, чтобы сгенерировать и отобразить список пар слов. По мере прокрутки пользователя список (отображаемый в виджете ListView) растет бесконечно. Конструктор builder ListView позволяет построить просмотр списка лениво, по требованию.

  1. Добавьте список предложений _suggestions в класс _RandomWordsState для сохранения предлагаемых пар слов. Также, добавьте переменную _biggerFont для увеличения размера шрифта.

lib/main.dart

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];
  final _biggerFont = TextStyle(fontSize: 18.0);
  // ···
}

Далее в класс _RandomWordsState будет добавлена функция _buildSuggestions(). Этот метод формирует ListView, который отображает предлагаемую пару слов.

Класс ListView предоставляет свойство конструктора, itemBuilder, это factory builder и функция обратного вызова, указанная как анонимная функция. Функции передаются два параметра — BuildContext и итератор строк, i. Итератор начинается с 0 и увеличивается каждый раз при вызове функции. Он инкрементируется дважды для каждой предложенной пары слов: один раз для ListTile, и один раз для Divider. Эта модель позволяет предлагаемому списку продолжать расти по мере прокрутки пользователя.

2. Добавьте функции _buildSuggestions() в класс _RandomWordsState:

lib/main.dart (_buildSuggestions)

Widget _buildSuggestions() {
  return ListView.builder(
      padding: EdgeInsets.all(16.0),
      itemBuilder: /*1*/ (context, i) {
        if (i.isOdd) return Divider(); /*2*/

        final index = i ~/ 2; /*3*/
        if (index >= _suggestions.length) {
          _suggestions.addAll(generateWordPairs().take(10)); /*4*/
        }
        return _buildRow(_suggestions[index]);
      });
}

/*1*/ Обратный вызов itemBuilder вызывается один раз на каждую предложенную пару слов и помещает каждое предложение в строку ListTile. Для четных строк функция добавляет строку ListTile для объединения слов. Для нечетных строк, функция добавляет виджет разделителя Divider для визуального разделения записей. Обратите внимание, что разделитель может быть трудно увидеть на небольших устройствах.

/*2*/ Добавление виджета разделителя Divider высотой в один пиксель перед каждой строкой в ListView.

/*3*/ Выражение i ~/ 2 делит i на 2 и возвращает целочисленный результат. Например: 1, 2, 3, 4, 5 становится 0, 1, 1, 2, 2. При этом вычисляется фактическое количество пар слов в ListView за вычетом виджетов разделителей.

/*4*/ Если Вы достигли конца доступных пар слов, сгенерируйте еще 10 и добавьте их в список предложений.

Функция _buildSuggestions() вызывает _buildRow() один раз на пару слов. Эта функция отображает каждую новую пару в ListTile, что позволяет сделать строки более привлекательными на следующем этапе.

3. Добавьте функцию _buildRow() в _RandomWordsState:

lib/main.dart (_buildRow)

Widget _buildRow(WordPair pair) {
  return ListTile(
    title: Text(
      pair.asPascalCase,
      style: _biggerFont,
    ),
  );
}

В классе _RandomWordsState обновите метод build() для использования _buildSuggestions(), а не для прямого вызова библиотеки генерации слов. (Scaffold реализует базовую визуальную компоновку Material Design.) Замените тело метода кодом:

lib/main.dart (build)

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('Startup Name Generator'),
    ),
    body: _buildSuggestions(),
  );
}

5. В классе MyApp обновите метод build(), изменив заголовок и превратив домашний виджет в виджет RandomWords:

{step3_stateful_widget → step4_infinite_list}/lib/main.dart

@@ -10,15 +10,8 @@
  class MyApp extends StatelessWidget {    
    @override            
    Widget build(BuildContext context) {   
      return MaterialApp(         
-       title: 'Welcome to Flutter',            
-       home: Scaffold(            
+       title: 'Startup Name Generator',            
+       home: RandomWords(),            
-         appBar: AppBar(            
-           title: Text('Welcome to Flutter'),            
-         ),            
-         body: Center(            
-           child: RandomWords(),            
-         ),            
-       ),            
      );            
    }

— убрать, + добавить

6. Перезапустите приложение. Вы должны увидеть список пар слов, независимо от того, как далеко вы прокручиваете.

Android
IOS

Проблемы?

Если ваше приложение работает некорректно, ищите опечатки. Если вы хотите попробовать некоторые из инструментов отладки Flutter’s, ознакомьтесь с набором инструментов отладки и профилирования DevTools. При необходимости используйте код, приведенный по следующей ссылке, чтобы вернуться к работе.

Профилирование или выпуски релизов

Важно: Не тестируйте производительность вашего приложения с включенной функцией отладки и горячей перезагрузки.

До сих пор вы запускали свое приложение в режиме отладки. В отладочном режиме производительность меняется на полезные функции разработчика, такие как горячая перезагрузка и пошаговая отладка. Неудивительно, что в отладочном режиме вы увидите медленную производительность и дрожащую анимацию. Как только вы будете готовы проанализировать производительность или выпустить ваше приложение, вы захотите использовать режимы сборки Flutter’s «profile» или «release». Подробности см. в разделе Режимы сборки Flutter.

Важно: Если вас беспокоит размер пакета вашего приложения, см. раздел Измерение размера приложения.

Следующие шаги

Поздравляем!

Вы написали интерактивное флаттер-приложение, которое работает как на iOS, так и на Android. В этой лабораторной работе вы:

  • Создали приложение «Flutter» с нуля.
  • Написали код на Dart-е.
  • Использовал внешнюю, стороннюю библиотеку.
  • Использовали горячую перезагрузку для более быстрого цикла разработки.
  • Реализовали statefull виджет.
  • Создали лениво загружаемый бесконечный список прокрутки.

Если вы хотите расширить это приложение, перейдите ко второй части на сайте Google Developers Codelabs, где вы добавите следующую функциональность:

  • Реализуете интерактивность, добавив иконку сердца с щелчком мыши, чтобы сохранить любимые пары.
  • Реализуете навигацию по новому маршруту, добавив новый экран с сохраненными избранными.
  • Измените цвета темы, сделав приложение полностью белым.
Приложение из части 2