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

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

Меня зовут Алексей, я основатель и разработчик системы автоматизации работы управляющих компаний «Оператор 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

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


ссылка на оригинал статьи https://habr.com/ru/post/683202/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *