Вопрос создания виджетов

Всем доброго дня!

Меня зовут Алексей, я основатель и разработчик системы автоматизации работы управляющих компаний «Оператор 18».

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

Как то я уже писал статью об Операторе18. Но тогда это была первая версия проекта, я собирал мнения, пробовал оценить рынок, выбрать вариант монетизации и т. д.

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

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

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

Для второй версии я решил заказать дизайн.

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

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

Изначально я был уверен что буду сам писать виджеты, прямо с нуля. Я считал что не стоит держать в проекте много сторонних зависимостей! Можешь если сам написать что-то — пиши!

Вот, например, окно входа в систему в новом дизайне выглядит так:

И я, решив прислушаться к себе, не стал искать готовых решений, а написал свой виджет:

class LoginDropdown extends StatefulWidget { final Function(String) onUserRoleChanged; const LoginDropdown({ required this.onUserRoleChanged, }); @override State<LoginDropdown> createState() => _LoginDropdownState(); } class _LoginDropdownState extends State<LoginDropdown> { bool isShowMenu = false; String currentRole = UserRole.mcOperator; Color roleDropdownButtonColor = AppColors.gray_3; @override Widget build( BuildContext context, ) => MouseRegion( onEnter: (_) => setState(() => roleDropdownButtonColor = AppColors.gray_1), onExit: (_) => setState(() => roleDropdownButtonColor = AppColors.gray_3), child: GestureDetector( onTap: () { setState(() { // ignore: avoid_bool_literals_in_conditional_expressions isShowMenu = isShowMenu ? false : true; }); }, child: Stack( children: [ Container( height: 56, width: 418, decoration: BoxDecoration( color: roleDropdownButtonColor, borderRadius: const BorderRadius.all( Radius.circular(12), ), ), child: Padding( padding: const EdgeInsets.only( left: 20, right: 20, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( currentRole, style: AppFonsts.dropDown_1, ), Image.asset( 'icons/dropdown_arrow.png', height: 20, width: 20, ), ], ), ), ), if (isShowMenu) Padding( // 56 - is height of first container, // 8 - is constraint between containers padding: const EdgeInsets.only(top: 56 + 8), child: Container( height: 166, width: 418, decoration: const BoxDecoration( boxShadow: [ // BoxShadow setup found here: // https://devsheet.com/code-snippet/add-box-shadow-to-container-in-flutter/ BoxShadow( color: AppColors.gray_6, blurRadius: 90, offset: Offset(0, 20), ) ], color: AppColors.white, borderRadius: BorderRadius.all( Radius.circular(12), ), ), child: Padding( padding: const EdgeInsets.symmetric(vertical: 14), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ for (var role in UserRole.list) LoginDropdownItem( onTap: () { setState(() { isShowMenu = false; currentRole = role; widget.onUserRoleChanged(role); }); }, userRole: role, ), ], ), ), ), ), ], ), ), ); }

Для меня «свой виджет» — это не просто кнопка, например, которой поменяли цвет и скруглили углы. «Свой виджет» — это виджет при создании которого нужно описывать его поведение и реакцию на действия пользователя.

Получилось хорошо, возможно не хватает какой нибудь анимации, но в целом — я остался доволен!

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

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

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

// Inside a Row widget DropdownButtonHideUnderline( child: DropdownButton2( icon: const SizedBox(), dropdownWidth: 418.w, dropdownMaxHeight: 233.h, dropdownDecoration: BoxDecoration( borderRadius: BorderRadius.circular(16.r), ), hint: Row( children: [ Text( AppString.more, style: AppFonts.menuUnselected, ), SizedBox( width: 5.w, ), Image.asset( 'icons/dropdown_arrow.png', height: 20.h, width: 20.w, ), ], ), items: items.map( (item) => DropdownMenuItem<String>( value: item, child: Text( item, style: AppFonts.dropDownBlack, ), ), ).toList(), value: selectedValue, onChanged: widget.onLogSelected, itemHeight: 35.h, itemPadding: EdgeInsets.only( left: 28.w, ), ), ), // Inside a Row widget

Спасибо за прочтение! Буду благодарен за критику/советы/иные комментарии.

11
2 комментария

Есть встроенный typedef:
final ValueChanged<String> onUserRoleChanged;

Дальше не буду, но есть много над чем работать 🤓

2
Ответить

Спасибо!
А если не секрет, почему дальше не будете? :) было бы полезно

Ответить