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

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

Шаги, связанные с процессом создания кнопки загрузки в приложении для разработки флаттера, упомянуты ниже:

  1. Определение нового виджета с отслеживанием состояния
  2. Определение возможных визуальных состояний кнопки
  3. Отобразить форму кнопки
  4. Создание отображения текста кнопки
  5. Отображать счетчик во время загрузки
  6. Отображать процент выполнения и кнопку остановки во время загрузки
  7. Добавить обратные вызовы при нажатии кнопки

1. Определение нового виджета с отслеживанием состояния

Внешний вид вашего виджета кнопки должен меняться со временем. В результате вам потребуется использовать специальный виджет без сохранения состояния для реализации вашей кнопки. Затем определите новый виджет без сохранения состояния с именем DownloadButton.

class DownloadButton расширяет StatelessWidget {

константа DownloadButton({

супер.ключ,

});

@переопределить

Сборка виджета (контекст BuildContext) {

// ДЕЛАТЬ:

возврат const SizedBox();

}

}

2. Определение возможных визуальных состояний кнопки

Текущий статус загрузки определяет визуальное отображение кнопки загрузки. После определения возможных состояний загрузки обновите DownloadButton, чтобы принять DownloadStatus и указать продолжительность, в течение которой кнопка должна переходить из одного визуального состояния в другое.

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

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

перечисление Статус загрузки {

Не скачано,

выборкаСкачать,

скачивание,

скачал,

}

3. Отобразите форму кнопки

Форма кнопки загрузки зависит от состояния загрузки. Во время фаз загрузки и загрузки кнопка отображается в виде серого прямоугольника со скругленными углами. На этапах загрузки и скачивания кнопка показывает полупрозрачный кружок.

На следующем шаге создайте AnimatedContainer с ShapeDecoration, который показывает прямоугольник со скругленными углами или круг на основе текущего состояния загрузки.

Подумайте о том, чтобы создать дерево виджетов фигуры в отдельном виджете без сохранения состояния, чтобы сохранить простоту основной функции build() и в то же время разрешить модификации, которые появятся позже. Затем вместо создания функции для возврата виджета, такой как Widget _buildSomething(), всегда создавайте StatelessWidget или StatefulWidget, что быстрее.

Теперь AnimatedContainer выглядит просто дочерним элементом SizedBox, но вам не о чем беспокоиться; мы разрешим это состояние на другом шаге.

@неизменный

classButtonShapeWidget расширяет StatelessWidget {

constButtonShapeWidget({

супер.ключ,

требуется это.искачивание,

требуется это.загружено,

требуется это.isFetching,

требуется это.transitionDuration,

});

конечный логический параметр загружается;

окончательный логический параметр загружен;

последнее логическое значение — выборка;

окончательная длительность transitionDuration;

@переопределить

Сборка виджета (контекст BuildContext) {

переменная форма = constShapeDecoration(

форма: StadiumBorder(),

цвет: CupertinoColors.lightBackgroundGray,

);

if (isDownloading || isFetching) {

форма = украшение формы (

форма: constCircleBorder(),

цвет: Colors.white.withOpacity(0.0),

);

}

возврат AnimatedContainer(

продолжительность:transitionDuration,

кривая: Curves.ease,

ширина: двойная бесконечность,

украшение: форма,

дочерний элемент: constSizedBox(),

);

}

}

4. Создание отображения текста кнопки

Различные сообщения отображаются с помощью кнопки «Загрузить» на разных этапах. Фаза notDownloaded отображает параметр GET. Загруженные фазы отображают для пользователей параметр OPEN, в то время как в промежуточном процессе текст не отображается.

Добавьте виджеты для отображения текста на каждом этапе загрузки и анимируйте непрозрачность текста между ними. Например, в кнопке виджет-оболочка сделайте дерево текстовых виджетов дочерним элементом AnimatedContainer.

вернуть AnimatedContainer(

продолжительность:transitionDuration,

кривая: Curves.ease,

ширина: двойная бесконечность,

украшение: форма,

ребенок: Заполнение(

заполнение: const EdgeInsets.симметричный (по вертикали: 6),

дочерний элемент: AnimatedOpacity(

продолжительность:transitionDuration,

непрозрачность: isDownloading || получение ? 0,0 : 1,0,

кривая: Curves.ease,

ребенок: Текст(

isDownloaded? «ОТКРЫТЬ» : «ПОЛУЧИТЬ»,

textAlign: TextAlign.center,

стиль: Theme.of(context).textTheme.button?.copyWith(

fontWeight: FontWeight.bold,

цвет: CupertinoColors.activBlue,

),

),

),

),

);

5. Отображение счетчика при загрузке

Кнопка DownloadButton показывает круговой счетчик во время загрузки. Этот счетчик переходит из режима notDownloaded в режим fetchingDownload.

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

Функция Object() { [собственный код] } в ButtonShapeWidget была удалена, чтобы сосредоточиться на ее функции построения и виджете стека, который мы создали. .

@переопределить

Сборка виджета (контекст BuildContext) {

возврат Детектор жестов(

onTap: _onPressed,

ребенок: Стек(

дети: [

ButtonShapeWidget(

длительность перехода: длительность перехода,

isDownloaded: _isDownloaded,

isDownloading: _isDownloading,

isFetching: _isFetching,

),

Позиционировано.заполнить(

дочерний элемент: AnimatedOpacity(

продолжительность:transitionDuration,

непрозрачность: _isDownloading || _isFetching ? 1,0 : 0,0,

кривая: Curves.ease,

дочерний элемент: ProgressIndicatorWidget(

скачатьПрогресс: скачатьПрогресс,

isDownloading: _isDownloading,

isFetching: _isFetching,

),

),

),

],

),

);

}

6. Отображать процент выполнения и кнопку остановки во время загрузки.

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

После добавления свойства расширения в подключаемый модуль DownloadButton измените отображение хода выполнения на круговую панель инструментов на этапе загрузки. Затем поместите значок кнопки остановки в центр радиального индикатора выполнения.

@переопределить

Сборка виджета (контекст BuildContext) {

возврат Детектор жестов(

onTap: _onPressed,

ребенок: Стек(

дети: [

ButtonShapeWidget(

длительность перехода: длительность перехода,

isDownloaded: _isDownloaded,

isDownloading: _isDownloading,

isFetching: _isFetching,

),

Позиционировано.заполнить(

дочерний элемент: AnimatedOpacity(

продолжительность:transitionDuration,

непрозрачность: _isDownloading || _isFetching ? 1,0 : 0,0,

кривая: Curves.ease,

ребенок: Стек(

выравнивание: Alignment.center,

дети: [

Виджет ProgressIndicator(

скачатьПрогресс: скачатьПрогресс,

isDownloading: _isDownloading,

isFetching: _isFetching,

),

если (_isDownloading)

Постоянная иконка(

Иконки.стоп,

размер: 14.0,

цвет: CupertinoColors.activeBlue,

),

],

),

),

),

],

),

);

}

7. Добавьте обратные вызовы к нажатиям кнопок

Поведение кнопки — это последняя вещь, которую требует DownloadButton. Когда пользователь нажимает кнопку, он должен выполнить какое-то действие. Добавьте обратные вызовы для начала загрузки, отмены загрузки и открытия загрузки в свойства виджета.

Наконец, используйте виджет GestureDetector, чтобы скрыть текущее дерево виджетов DownloadButton и направить событие касания в соответствующее свойство обратного вызова.

недействительным _onPressed () {

переключатель (статус) {

случай DownloadStatus.notDownloaded:

при загрузке();

перерыв;

case DownloadStatus.fetchingDownload:

// ничего не делать.

перерыв;

case DownloadStatus.downloading:

приОтмене();

перерыв;

случай DownloadStatus.downloaded:

при открытии();

перерыв;

}

}

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

Давайте посмотрим на полный пример DownloadButton:

импортировать ‘package:flutter/cupertino.dart’;

импортировать ‘package:flutter/material.dart’;

void main() {

запустить приложение(

константное приложение MaterialApp(

дома: ПримерЗагрузитьКнопку(),

debugShowCheckedModeBanner: false,

),

);

}

@неизменный

класс ExampleDownloadButton расширяет StatefulWidget {

const ПримерЗагрузитьКнопку();

@переопределить

_ExampleDownloadButtonState createState() => _ExampleDownloadButtonState();

}

класс _ExampleDownloadButtonState расширяет состояние‹exampledownloadbutton› {

поздний финал List‹downloadcontroller› _downloadControllers;

@переопределить

void initState() {

супер.initState();

_downloadControllers = Список‹контроллер загрузки›.generate(

10,

(индекс) => SimulatedDownloadController(onOpenDownload: () {

_openDownload(индекс);

}),

);

}

void _openDownload(int index) {

ScaffoldMessenger.of(context).showSnackBar(

Закусочная(

содержимое: Текст('Открыть PDF ${index + 1}'),

),

);

}

@переопределить

Сборка виджета (контекст BuildContext) {

возврат Леска(

appBar: AppBar(title: const Text('Кнопка загрузки')),

тело: ListView.separated(

itemCount: _downloadControllers.length,

separatorBuilder: (_, __) => const Divider(),

itemBuilder: _buildListItem,

),

);

}

Виджет _buildListItem (контекст BuildContext, индекс int) {

окончательная тема = Theme.of (контекст);

окончательный контроллер загрузки = _downloadControllers[index];

возврат ListTile(

ведущий: константный значок (Icons.list_rounded),

Название: Текст(

‘Pdf ${индекс + 1}’,

переполнение: TextOverflow.ellipsis,

стиль: theme.textTheme.headline6,

),

замыкающий: SizeBox(

ширина: 96,

дочерний элемент: AnimatedBuilder(

анимация: скачатьконтроллер,

строитель: (контекст, дочерний элемент) {

возврат DownloadButton(

статус: downloadController.downloadStatus,

DownloadProgress: downloadController.progress,

onDownload: downloadController.startDownload,

onCancel: downloadController.stopDownload,

onOpen: скачатьController.openDownload,

);

},

),

),

);

}

}

перечисление Статус загрузки {

Не скачано,

выборкаСкачать,

скачивание,

скачал,

}

абстрактный класс DownloadController реализует ChangeNotifier {

Получить статус загрузки;

удвоить прогресс;

void startDownload();

void stopDownload();

void openDownload();

}

класс SimulatedDownloadController расширяет DownloadController

с уведомлением об изменении {

SimulatedDownloadController({

DownloadStatus downloadStatus = DownloadStatus.notDownloaded,

двойной прогресс = 0,0,

требуется VoidCallback при OpenDownload,

}) : _downloadStatus = статус загрузки,

_progress = прогресс,

_onOpenDownload = onOpenDownload;

Статус загрузки _downloadStatus;

@переопределить

DownloadStatus получить статус загрузки => _downloadStatus;

двойной _прогресс;

@переопределить

двойное получение прогресса => _progress;

окончательный VoidCallback _onOpenDownload;

bool _isDownloading = false;

@переопределить

void startDownload() {

if (downloadStatus == DownloadStatus.notDownloaded) {

_doSimulatedDownload();

}

}

@переопределить

void stopDownload() {

if (_isDownloading) {

_isDownloading = false;

_downloadStatus = DownloadStatus.notDownloaded;

_прогресс = 0,0;

уведомитьслушателей();

}

}

@переопределить

void openDownload() {

if (downloadStatus == DownloadStatus.downloaded) {

_onOpenDownload();

}

}

Future‹void› _doSimulatedDownload() async {

_isDownloading = true;

_downloadStatus = DownloadStatus.fetchingDownload;

уведомитьслушателей();

// Подождите секунду, чтобы имитировать время выборки.

await Future‹void›.delayed(const Duration(seconds: 1));

// Если пользователь решил отменить загрузку, остановите симуляцию.

if (!_isDownloading) {

возврат;

}

// Переходим к этапу загрузки.

_downloadStatus = DownloadStatus.downloading;

уведомитьслушателей();

const downloadProgressStops = [0,0, 0,15, 0,45, 0,8, 1,0];

для (конечная остановка в downloadProgressStops) {

// Подождите секунду, чтобы имитировать различную скорость загрузки.

// ожидание Future‹void›.delayed(const Duration(seconds: 1));

if (!_isDownloading) {

возврат;

}

_прогресс = стоп;

уведомитьслушателей();

}

await Future‹void›.delayed(const Duration(seconds: 1));

if (!_isDownloading) {

возврат;

}

_downloadStatus = DownloadStatus.downloaded;

_isDownloading = false;

уведомитьслушателей();

}

}

@неизменный

класс DownloadButton расширяет StatelessWidget {

константа DownloadButton({

требуется этот.статус,

это.downloadProgress = 0,0,

требуется это.onDownload,

требуется этот.onCancel,

требуется это.onOpen,

this.transitionDuration = const Duration (миллисекунды: 500),

});

окончательный статус DownloadStatus;

окончательная двойная загрузка Progress;

окончательный VoidCallback при загрузке;

окончательный VoidCallback onCancel;

окончательный VoidCallback при открытии;

окончательная длительность transitionDuration;

bool get _isDownloading =› status == DownloadStatus.downloading;

bool get _isFetching => status == DownloadStatus.fetchingDownload;

bool get _isDownloaded =› status == DownloadStatus.downloaded;

void _onPressed() {

переключатель (статус) {

случай DownloadStatus.notDownloaded:

при загрузке();

перерыв;

case DownloadStatus.fetchingDownload:

// ничего не делать.

перерыв;

case DownloadStatus.downloading:

приОтмене();

перерыв;

случай DownloadStatus.downloaded:

при открытии();

перерыв;

}

}

@переопределить

Сборка виджета (контекст BuildContext) {

возврат Детектор жестов(

onTap: _onPressed,

ребенок: Стек(

дети: [

ButtonShapeWidget(

длительность перехода: длительность перехода,

isDownloaded: _isDownloaded,

isDownloading: _isDownloading,

isFetching: _isFetching,

),

Позиционировано.заполнить(

дочерний элемент: AnimatedOpacity(

продолжительность:transitionDuration,

непрозрачность: _isDownloading || _isFetching ? 1,0 : 0,0,

кривая: Curves.ease,

ребенок: Стек(

выравнивание: Alignment.center,

дети: [

Виджет ProgressIndicator(

скачатьПрогресс: скачатьПрогресс,

isDownloading: _isDownloading,

isFetching: _isFetching,

),

если (_isDownloading)

Постоянная иконка(

Иконки.стоп,

размер: 14,

цвет: CupertinoColors.activeBlue,

),

],

),

),

),

],

),

);

}

}

@неизменный

класс ButtonShapeWidget расширяет StatelessWidget {

константа ButtonShapeWidget({

ключ,

требуется это.isDownloading,

требуется это.isDownloaded,

требуется это.isFetching,

требуется это.transitionDuration,

});

конечный логический параметр загружается;

окончательный логический параметр загружен;

последнее логическое значение — выборка;

окончательная длительность transitionDuration;

@переопределить

Сборка виджета (контекст BuildContext) {

var shape = const ShapeDecoration(

форма: StadiumBorder(),

цвет: CupertinoColors.lightBackgroundGray,

);

if (isDownloading || isFetching) {

форма = украшение формы (

форма: const CircleBorder(),

цвет: Colors.white.withOpacity(0),

);

}

возврат AnimatedContainer(

продолжительность:transitionDuration,

кривая: Curves.ease,

ширина: двойная бесконечность,

украшение: форма,

ребенок: Заполнение(

заполнение: const EdgeInsets.симметричный (по вертикали: 6),

дочерний элемент: AnimatedOpacity(

продолжительность:transitionDuration,

непрозрачность: isDownloading || получение ? 0,0 : 1,0,

кривая: Curves.ease,

ребенок: Текст(

загружен? «ОТКРЫТЬ»: «ПОЛУЧИТЬ»,

textAlign: TextAlign.center,

стиль: Theme.of(context).textTheme.button?.copyWith(

fontWeight: FontWeight.bold,

цвет: CupertinoColors.activeBlue,

),

),

),

),

);

}

}

@неизменный

класс ProgressIndicatorWidget расширяет StatelessWidget {

константа ProgressIndicatorWidget({

ключ,

требуется это.downloadProgress,

требуется это.isDownloading,

требуется это.isFetching,

});

окончательная двойная загрузка Progress;

конечный логический параметр загружается;

последнее логическое значение — выборка;

@переопределить

Сборка виджета (контекст BuildContext) {

возврат Соотношение сторон(

соотношение сторон: 1,

дочерний: TweenAnimationBuilder‹двойной›(

tween: Tween (начало: 0, конец: downloadProgress),

продолжительность: const Duration (миллисекунды: 200),

строитель: (контекст, прогресс, дочерний элемент) {

возврат CircularProgressIndicator(

backgroundColor: isСкачивание

? CupertinoColors.lightBackgroundGray

: Цвета.белый.с непрозрачностью(0),

valueColor: AlwaysStoppedAnimation (isFetching

? CupertinoColors.lightBackgroundGray

: CupertinoColors.activeBlue),

ширина хода: 2,

значение: isFetching ? null : прогресс,

);

},

),

);

}

}‹/double›‹/void›‹/void›‹/void›‹/void›‹ /downloadcontroller›‹/downloadcontroller›‹/exampledownloadbutton›

Выход

Заключение

Итак, мы узнали, как создать кнопку загрузки во Flutter. Мы рассмотрели 7-шаговый процесс создания кнопки загрузки. Спасибо за прочтение статьи. Надеюсь, вам понравится наш контент. Продолжайте посещать решения Flutter Agency для разработки бизнес-приложений на Flutter.