Skip to content

Commit

Permalink
finish dropdown
Browse files Browse the repository at this point in the history
  • Loading branch information
tilucasoli committed Dec 18, 2024
1 parent afb344d commit 29ca8f1
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 57 deletions.
5 changes: 5 additions & 0 deletions packages/remix/demo/lib/components/dropdown_menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ class _DropdownMenuDemoState extends State<DropdownMenuDemo> {
});
},
),
onPressOutside: () {
setState(() {
open = false;
});
},
open: open,
items: [
const DropdownMenuItem(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:mix/mix.dart';
import 'package:mix_annotations/mix_annotations.dart';

import '../../core/theme/remix_theme.dart';
import '../../helpers/object_ext.dart';
import '../../helpers/overlay.dart';

part 'dropdown_menu.g.dart';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class DropdownMenuStyle extends SpecStyle<DropdownMenuSpecUtility> {
final menuStyle = [
$.menu.container.chain
..borderRadius(6)
..shadow.color(Colors.black.withOpacity(0.07))
..shadow.color.black.withOpacity(0.07)
..shadow.blurRadius(5)
..color.white()
..border.color.black12()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class DropdownMenu extends StatefulWidget {
}

class DropdownMenuState extends State<DropdownMenu> {
final OverlayPortalController _tooltipController = OverlayPortalController();
late final MixWidgetStateController _menuStateController;

final _link = LayerLink();
Expand All @@ -55,33 +54,15 @@ class DropdownMenuState extends State<DropdownMenu> {
void initState() {
super.initState();

_menuStateController = MixWidgetStateController()..selected = false;

if (widget.open) {
WidgetsBinding.instance.addPostFrameCallback((_) {
show();
});
}
}

void _onEndAnimation() {
if (_menuStateController.selected == false) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_tooltipController.hide();
});
}
_menuStateController = MixWidgetStateController()..selected = widget.open;
}

@override
void didUpdateWidget(DropdownMenu oldWidget) {
super.didUpdateWidget(oldWidget);

if (widget.open) {
WidgetsBinding.instance.addPostFrameCallback((_) {
show();
});
} else {
hide();
if (widget.open != oldWidget.open) {
_menuStateController.selected = widget.open;
}
}

Expand All @@ -93,11 +74,15 @@ class DropdownMenuState extends State<DropdownMenu> {

@override
Widget build(BuildContext context) {
final style = widget.style ?? context.remix.components.dropdownMenu;
final dropdownMenuStyle =
widget.style ?? context.remix.components.dropdownMenu;
final configuration =
SpecConfiguration(context, DropdownMenuSpecUtility.self);
final appliedStyle =
style.makeStyle(configuration).applyVariants(widget.variants);
final appliedStyle = dropdownMenuStyle
.makeStyle(configuration)
.applyVariants(widget.variants);

final animatedStyle = appliedStyle.cast<AnimatedStyle>();

return OverlayWrapper(
target: RepaintBoundary(child: widget.trigger),
Expand All @@ -113,14 +98,10 @@ class DropdownMenuState extends State<DropdownMenu> {
),
);

final AnimatedStyle? animatedStyle =
appliedStyle is AnimatedStyle ? appliedStyle : null;

return AnimatedBoxSpecWidget(
spec: FlexContainer.box,
duration: animatedStyle?.animated.duration ?? Duration.zero,
curve: animatedStyle?.animated.curve ?? Curves.easeInOut,
onEnd: _onEndAnimation,
child: FlexContainer.flex(
direction: Axis.vertical,
children: List.generate(widget.items.length, (index) {
Expand All @@ -132,31 +113,9 @@ class DropdownMenuState extends State<DropdownMenu> {
);
},
),
controller: _tooltipController,
onTapOutside: widget.onPressOutside,
// onPressOutside: hide,
showOverlay: widget.open,
animationDuration: animatedStyle?.animated.duration ?? Duration.zero,
);
}

void show() {
if (!_tooltipController.isShowing) {
_tooltipController.show();
}

WidgetsBinding.instance.addPostFrameCallback((_) {
_menuStateController.selected = true;
});
}

void hide() {
_menuStateController.selected = false;
}

void toggleMenu() {
if (!_tooltipController.isShowing) {
show();
} else {
hide();
}
}
}
5 changes: 5 additions & 0 deletions packages/remix/lib/src/helpers/object_ext.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extension ObjectExtension on Object {
T? cast<T>() {
return this is T ? this as T : null;
}
}
50 changes: 47 additions & 3 deletions packages/remix/lib/src/helpers/overlay.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:async';

import 'package:flutter/material.dart';

class OverlayWrapper extends StatefulWidget {
Expand All @@ -8,9 +10,10 @@ class OverlayWrapper extends StatefulWidget {
this.offset = const Offset(0, 4),
this.targetAnchor = Alignment.bottomCenter,
this.followerAnchor = Alignment.topCenter,
required this.controller,
this.controller,
this.onTapOutside,
this.showOverlay = true,
this.animationDuration = const Duration(),
});

/// The trigger widget that opens the dropdown menu.
Expand All @@ -30,26 +33,67 @@ class OverlayWrapper extends StatefulWidget {
final Alignment followerAnchor;

/// The controller that controls the overlay.
final OverlayPortalController controller;
final OverlayPortalController? controller;

/// Whether the overlay should be shown.
final bool showOverlay;

final VoidCallback? onTapOutside;

final Duration animationDuration;

@override
State<OverlayWrapper> createState() => OverlayWrapperState();
}

class OverlayWrapperState extends State<OverlayWrapper> {
final _link = LayerLink();
OverlayPortalController? _controller;
Timer? _timer;

@override
void initState() {
super.initState();

_controller = widget.controller ?? OverlayPortalController();

if (widget.showOverlay) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_controller!.show();
});
}
}

@override
void didUpdateWidget(OverlayWrapper oldWidget) {
super.didUpdateWidget(oldWidget);

if (widget.showOverlay) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_controller!.show();
});
} else {
if (_timer != null) {
_timer!.cancel();
}
_timer = Timer(widget.animationDuration, () {
_controller!.hide();
});
}
}

@override
void dispose() {
_timer?.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
return CompositedTransformTarget(
link: _link,
child: OverlayPortal(
controller: widget.controller,
controller: _controller!,
overlayChildBuilder: (BuildContext context) {
return Stack(children: [
if (widget.onTapOutside != null)
Expand Down

0 comments on commit 29ca8f1

Please sign in to comment.