Всем доброго дня!
Я 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
Спасибо за прочтение! Буду благодарен за критику/советы/иные комментарии.