Skip to content

Commit

Permalink
feat: multiple select dropdown (#198)
Browse files Browse the repository at this point in the history
  • Loading branch information
fedecor9 authored Jun 4, 2024
1 parent 0ccaaf7 commit 9f83182
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 13 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ mason-lock.json
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
.vscode/
.vscode/settings.json

# Flutter/Dart/Pub related
Expand Down
2 changes: 1 addition & 1 deletion design_system/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ migrate_working_dir/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
.vscode/

# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
Expand Down
2 changes: 1 addition & 1 deletion design_system/design_system_gallery/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ migrate_working_dir/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
.vscode/

# Flutter/Dart/Pub related
**/doc/api/
Expand Down
2 changes: 1 addition & 1 deletion design_system/design_system_gallery/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189

COCOAPODS: 1.12.1
COCOAPODS: 1.14.3
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
//ignore_for_file: unused-files, unused-code
import 'package:auto_route/auto_route.dart';
import 'package:design_system/widgets/app_dropdown.dart';
import 'package:design_system/widgets/app_select_dropdown.dart';
import 'package:design_system_gallery/gallery/gallery_scaffold_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

@RoutePage()
class GalleryDropdownScreen extends StatelessWidget {
Expand All @@ -14,17 +16,18 @@ class GalleryDropdownScreen extends StatelessWidget {
margin: const EdgeInsets.all(20),
child: Column(
children: [
AppDropdownMenu<int>(
initialValue: 1,
dropdownMenuEntries: const [
SizedBox(height: 30.h),
AppSelectDropdown<int>(
label: 'Select',
items: const [
(value: 1, label: 'Option 1'),
(value: 2, label: 'Option 2'),
(value: 3, label: 'Option 3'),
(value: 4, label: 'Option 4'),
(value: 5, label: 'Option 5'),
(value: 6, label: 'Option 6'),
],
onSelected: (int? value) {},
onChanged: (int? value) {},
),
],
),
Expand Down
7 changes: 4 additions & 3 deletions design_system/lib/design_system.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
library design_system;

export 'theme/custom_colors.dart';
export 'theme/app_text_styles.dart';
export 'extensions/context_extensions.dart';
export 'theme/app_dimensions.dart';
export 'theme/app_text_styles.dart';
export 'theme/app_theme.dart';
export 'extensions/context_extensions.dart';
export 'theme/custom_colors.dart';
export 'widgets/app_select_dropdown.dart';
1 change: 0 additions & 1 deletion design_system/lib/theme/app_text_styles.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ class AppTextStyles extends TextTheme {
GoogleFonts.roboto(
fontSize: fontSize,
fontWeight: fontWeight,
color: Colors.white,
);

static AppTextStyles getDefaultAppStyles() => AppTextStyles.fromTextTheme(
Expand Down
167 changes: 167 additions & 0 deletions design_system/lib/widgets/app_select_dropdown.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import 'package:design_system/design_system.dart';
import 'package:design_system/extensions/color_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

typedef MenuItems<T> = ({T value, String label});

const _kAnimationDuration = Duration(milliseconds: 200);

class AppSelectDropdown<T> extends StatefulWidget {
final double? width;
final String label;
final List<MenuItems<T>> items;
final Function(T value)? onChanged;

const AppSelectDropdown({
required this.items,
required this.label,
this.onChanged,
this.width,
super.key,
});

@override
State<AppSelectDropdown<T>> createState() => _AppSelectDropdownState<T>();
}

class _AppSelectDropdownState<T> extends State<AppSelectDropdown<T>>
with SingleTickerProviderStateMixin {
late final FocusNode _buttonFocusNode;
final MenuController _menuController = MenuController();
final List<MenuItems<T>> _selectedValues = [];
late AnimationController _animationController;
static final Animatable<double> _easeInTween =
CurveTween(curve: Curves.easeIn);
static final Animatable<double> _halfTween =
Tween<double>(begin: 0.0, end: 0.5);

// ignore: unused_field
late Animation<double> _iconTurns;
@override
void initState() {
_buttonFocusNode = FocusNode(debugLabel: 'Menu Button-${widget.label}');
super.initState();
_animationController = AnimationController(
duration: _kAnimationDuration,
vsync: this,
);

_iconTurns = _animationController.drive(_halfTween.chain(_easeInTween));
}

@override
void dispose() {
_buttonFocusNode.dispose();
_animationController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) => MenuAnchor(
childFocusNode: _buttonFocusNode,
menuChildren: widget.items
.map(
(elem) => CheckboxMenuButton(
closeOnActivate: false,
value: _selectedValues.contains(elem),
onChanged: (value) {
widget.onChanged?.call(elem.value);
setState(() {
if (value!) {
_selectedValues.add(elem);
} else {
_selectedValues.remove(elem);
}
});
},
child: Container(
width: _calculateWidth(),
constraints: BoxConstraints(
minWidth: 90.w,
),
child: Text(
elem.label,
style: context.theme.textStyles.bodyMedium,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
),
)
.toList(),
controller: _menuController,
builder: (
BuildContext context,
MenuController controller,
Widget? child,
) =>
Container(
constraints: BoxConstraints(
minWidth: 90.w,
maxWidth: 1.sw,
),
decoration: BoxDecoration(
color: context.theme.colorScheme.surface.getShade(100),
borderRadius: BorderRadius.circular(4.r),
border: Border.all(
color: context.theme.colorScheme.onSurface.getShade(200),
),
),
child: InkWell(
onTap: () {
if (controller.isOpen) {
controller.close();
_animationController.reverse();
} else {
controller.open();
_animationController.forward();
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Container(
width: widget.width ?? 1.sw,
padding: EdgeInsets.all(8.sp),
child: Text(
_selectedValues.isEmpty
? widget.label
: _selectedValues.map((e) => e.label).join(', '),
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: context.theme.textStyles.labelMedium,
),
),
),
AnimatedBuilder(
animation: _animationController,
builder: (context, child) => Padding(
padding: EdgeInsets.all(8.sp),
child: RotationTransition(
turns: _iconTurns,
child: Icon(
Icons.expand_more,
color: context.theme.customColors.textColor,
),
),
),
),
],
),
),
),
);

double _calculateWidth() {
final width = widget.width;
if (width == null) return 1.sw * .761;
if (width <= .3.sw) return width * .9;
if (width <= .5.sw) return width * .93;
if (width <= .8.sw) return width * .95;
if (width < 1.sw) return width * .8;
return width * .761;
}
}
2 changes: 1 addition & 1 deletion lib/ui/main/main_screen.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import 'package:design_system/theme/app_theme.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_template/core/di/di_provider.dart';
import 'package:flutter_template/ui/resources.dart';
import 'package:flutter_template/ui/router/app_router.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

class MainScreen extends StatelessWidget {
const MainScreen({super.key});
Expand Down

0 comments on commit 9f83182

Please sign in to comment.