diff --git a/packages/dropdown_button2/CHANGELOG.md b/packages/dropdown_button2/CHANGELOG.md index d96c1ea..0297970 100644 --- a/packages/dropdown_button2/CHANGELOG.md +++ b/packages/dropdown_button2/CHANGELOG.md @@ -15,6 +15,8 @@ - Fix DropdownButtonFormField clips text when large text scale is used [Flutter core]. - Fix DropdownButtonFormField padding when ButtonTheme.alignedDropdown is true [Flutter core]. - Add barrierCoversButton to DropdownButtonFormField2. +- Respect button's borderRadius when barrierCoversButton is false. +- Respect inputDecoration's borderRadius when barrierCoversButton is false. ## 3.0.0-beta.21 diff --git a/packages/dropdown_button2/lib/src/dropdown_button2.dart b/packages/dropdown_button2/lib/src/dropdown_button2.dart index 01f40df..9d08a07 100644 --- a/packages/dropdown_button2/lib/src/dropdown_button2.dart +++ b/packages/dropdown_button2/lib/src/dropdown_button2.dart @@ -127,7 +127,8 @@ class DropdownButton2 extends StatefulWidget { 'Only one of valueListenable or multiValueListenable can be used.', ), _inputDecoration = null, - _isEmpty = false; + _isEmpty = false, + _hasError = false; const DropdownButton2._formField({ super.key, @@ -161,9 +162,11 @@ class DropdownButton2 extends StatefulWidget { required this.openDropdownListenable, required InputDecoration inputDecoration, required bool isEmpty, + required bool hasError, }) : underline = null, _inputDecoration = inputDecoration, - _isEmpty = isEmpty; + _isEmpty = isEmpty, + _hasError = hasError; /// The list of items the user can select. /// @@ -382,6 +385,8 @@ class DropdownButton2 extends StatefulWidget { final bool _isEmpty; + final bool _hasError; + @override State> createState() => _DropdownButton2State(); } @@ -572,6 +577,30 @@ class _DropdownButton2State extends State> with WidgetsBin } } + Set _materialState(InputDecoration decoration) => { + if (!decoration.enabled) WidgetState.disabled, + if (_isFocused) WidgetState.focused, + if (_isHovering) WidgetState.hovered, + if (widget._hasError) WidgetState.error, + }; + + BorderRadius? _getInputDecorationBorderRadius(InputDecoration decoration) { + InputBorder? border; + if (!decoration.enabled) { + border = widget._hasError ? decoration.errorBorder : decoration.disabledBorder; + } else if (_isFocused) { + border = widget._hasError ? decoration.focusedErrorBorder : decoration.focusedBorder; + } else { + border = widget._hasError ? decoration.errorBorder : decoration.enabledBorder; + } + border ??= WidgetStateProperty.resolveAs(decoration.border, _materialState(decoration)); + + if (border is OutlineInputBorder) { + return border.borderRadius; + } + return null; + } + EdgeInsetsGeometry _buttonAdditionalHPadding() { final TextDirection? textDirection = Directionality.maybeOf(context); @@ -602,6 +631,9 @@ class _DropdownButton2State extends State> with WidgetsBin _dropdownRoute = _DropdownRoute( items: items, buttonRect: _buttonRect, + buttonBorderRadius: widget._inputDecoration != null + ? _getInputDecorationBorderRadius(widget._inputDecoration!) + : _getButtonBorderRadius(context), selectedIndex: _selectedIndex ?? 0, isNoSelectedItem: _selectedIndex == null, onChanged: widget.onChanged, @@ -1026,6 +1058,10 @@ class DropdownButtonFormField2 extends FormField { : effectiveHint != null || effectiveDisabledHint != null; final bool isEmpty = !showSelectedItem && !isHintOrDisabledHintAvailable; + final bool hasError = field.hasError || + effectiveDecoration.errorText != null || + effectiveDecoration.error != null; + // An unFocusable Focus widget so that this widget can detect if its // descendants have focus or not. return Focus( @@ -1067,6 +1103,7 @@ class DropdownButtonFormField2 extends FormField { hintText: effectiveDecoration.hintText != null ? '' : null, ), isEmpty: isEmpty, + hasError: hasError, ), ), ); diff --git a/packages/dropdown_button2/lib/src/dropdown_route.dart b/packages/dropdown_button2/lib/src/dropdown_route.dart index 913d4aa..f0fb190 100644 --- a/packages/dropdown_button2/lib/src/dropdown_route.dart +++ b/packages/dropdown_button2/lib/src/dropdown_route.dart @@ -4,6 +4,7 @@ class _DropdownRoute extends PopupRoute<_DropdownRouteResult> { _DropdownRoute({ required this.items, required this.buttonRect, + required this.buttonBorderRadius, required this.selectedIndex, required this.isNoSelectedItem, required this.onChanged, @@ -25,6 +26,7 @@ class _DropdownRoute extends PopupRoute<_DropdownRouteResult> { final List> items; final ValueNotifier buttonRect; + final BorderRadius? buttonBorderRadius; final int selectedIndex; final bool isNoSelectedItem; final ValueChanged? onChanged; @@ -89,10 +91,11 @@ class _DropdownRoute extends PopupRoute<_DropdownRouteResult> { return barrierCoversButton ? routePage : _CustomModalBarrier( - animation: animation, barrierColor: _altBarrierColor, + animation: animation, barrierCurve: barrierCurve, buttonRect: rect, + buttonBorderRadius: buttonBorderRadius ?? BorderRadius.zero, child: routePage, ); }, @@ -458,18 +461,20 @@ class _DropdownRouteResult { /// It's used instead of the route barrier when `barrierCoversButton` is set to false. class _CustomModalBarrier extends StatefulWidget { const _CustomModalBarrier({ - this.animation, - this.barrierColor, + required this.barrierColor, + required this.animation, required this.barrierCurve, + required this.buttonRect, + required this.buttonBorderRadius, required this.child, - this.buttonRect, }); - final Animation? animation; final Color? barrierColor; + final Animation? animation; final Curve barrierCurve; + final Rect buttonRect; + final BorderRadius buttonBorderRadius; final Widget child; - final Rect? buttonRect; @override State<_CustomModalBarrier> createState() => _CustomModalBarrierState(); @@ -503,8 +508,9 @@ class _CustomModalBarrierState extends State<_CustomModalBarrier> { builder: (BuildContext context, Color? value, Widget? child) { return CustomPaint( painter: _DropdownBarrierPainter( - buttonRect: widget.buttonRect, barrierColor: value, + buttonRect: widget.buttonRect, + buttonBorderRadius: widget.buttonBorderRadius, pageSize: size, ), ); @@ -518,29 +524,47 @@ class _CustomModalBarrierState extends State<_CustomModalBarrier> { class _DropdownBarrierPainter extends CustomPainter { const _DropdownBarrierPainter({ - this.buttonRect, - this.barrierColor, + required this.barrierColor, + required this.buttonRect, + required this.buttonBorderRadius, required this.pageSize, }); - final Rect? buttonRect; final Color? barrierColor; + final Rect buttonRect; + final BorderRadius buttonBorderRadius; final Size pageSize; @override void paint(Canvas canvas, Size size) { - if (barrierColor != null && buttonRect != null) { - final Rect rect = - Rect.fromLTRB(-buttonRect!.left, -buttonRect!.top, pageSize.width, pageSize.height); + if (barrierColor != null) { + final Rect rect = Rect.fromLTRB( + -buttonRect.left, + -buttonRect.top, + pageSize.width, + pageSize.height, + ); + canvas.saveLayer(rect, Paint()); canvas.drawRect(rect, Paint()..color = barrierColor!); - canvas.drawRect(buttonRect!, Paint()..blendMode = BlendMode.clear); + + final RRect buttonRRect = RRect.fromRectAndCorners( + buttonRect, + topLeft: buttonBorderRadius.topLeft, + topRight: buttonBorderRadius.topRight, + bottomLeft: buttonBorderRadius.bottomLeft, + bottomRight: buttonBorderRadius.bottomRight, + ); + + canvas.drawRRect(buttonRRect, Paint()..blendMode = BlendMode.clear); canvas.restore(); } } @override bool shouldRepaint(_DropdownBarrierPainter oldPainter) { - return oldPainter.buttonRect != buttonRect || oldPainter.barrierColor != barrierColor; + return oldPainter.buttonRect != buttonRect || + oldPainter.barrierColor != barrierColor || + oldPainter.buttonBorderRadius != buttonBorderRadius; } }