Как стать автором
Обновить

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

Время на прочтение3 мин
Количество просмотров2.1K

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

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


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

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

Вот, например, окно входа в какую нибудь систему:

Пример окна для входа
Пример окна для входа

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

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

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

Теги:
Хабы:
Всего голосов 1: ↑1 и ↓0+1
Комментарии6

Публикации

Истории

Работа

Swift разработчик
15 вакансий
iOS разработчик
15 вакансий

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань