gpt4 book ai didi

flutter - 在 flutter 中创建自定义下拉列表 - 或者如何将自定义下拉选项放在其他所有内容之上的图层中

转载 作者:行者123 更新时间:2023-12-04 11:51:03 27 4
gpt4 key购买 nike

我正在寻找一种创建自定义下拉列表的方法,以便我可以自己设置样式。
我遇到了这个似乎非常有用的答案
https://stackoverflow.com/a/63165793/3808307
问题是如果容器小于下拉菜单,flutter 会提示像素溢出。我怎样才能让这个下拉菜单位于页面中其他元素的顶部,所以我没有收到这个警告?或者是否有另一种方法可以在没有此问题的情况下重新创建自定义下拉列表?
我找到的所有答案都是关于内置 DropdownButton
下面,上面链接的答案,带有版本

首先,创建一个名为 drop_list_model.dart 的 dart 文件:

import 'package:flutter/material.dart';

class DropListModel {
DropListModel(this.listOptionItems);

final List<OptionItem> listOptionItems;
}

class OptionItem {
final String id;
final String title;

OptionItem({@required this.id, @required this.title});
}
接下来,创建文件 file select_drop_list.dart :
import 'package:flutter/material.dart';
import 'package:time_keeping/model/drop_list_model.dart';
import 'package:time_keeping/widgets/src/core_internal.dart';

class SelectDropList extends StatefulWidget {
final OptionItem itemSelected;
final DropListModel dropListModel;
final Function(OptionItem optionItem) onOptionSelected;

SelectDropList(this.itemSelected, this.dropListModel, this.onOptionSelected);

@override
_SelectDropListState createState() => _SelectDropListState(itemSelected, dropListModel);
}

class _SelectDropListState extends State<SelectDropList> with SingleTickerProviderStateMixin {

OptionItem optionItemSelected;
final DropListModel dropListModel;

AnimationController expandController;
Animation<double> animation;

bool isShow = false;

_SelectDropListState(this.optionItemSelected, this.dropListModel);

@override
void initState() {
super.initState();
expandController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 350)
);
animation = CurvedAnimation(
parent: expandController,
curve: Curves.fastOutSlowIn,
);
_runExpandCheck();
}

void _runExpandCheck() {
if(isShow) {
expandController.forward();
} else {
expandController.reverse();
}
}

@override
void dispose() {
expandController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Container(
child: Column(
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 17),
decoration: new BoxDecoration(
borderRadius: BorderRadius.circular(20.0),
color: Colors.white,
boxShadow: [
BoxShadow(
blurRadius: 10,
color: Colors.black26,
offset: Offset(0, 2))
],
),
child: new Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(Icons.card_travel, color: Color(0xFF307DF1),),
SizedBox(width: 10,),

child: GestureDetector(
onTap: () {
this.isShow = !this.isShow;
_runExpandCheck();
setState(() {

});
},
child: Text(optionItemSelected.title, style: TextStyle(
color: Color(0xFF307DF1),
fontSize: 16),),
),

Align(
alignment: Alignment(1, 0),
child: Icon(
isShow ? Icons.arrow_drop_down : Icons.arrow_right,
color: Color(0xFF307DF1),
size: 15,
),
),
],
),
),
SizeTransition(
axisAlignment: 1.0,
sizeFactor: animation,
child: Container(
margin: const EdgeInsets.only(bottom: 10),
padding: const EdgeInsets.only(bottom: 10),
decoration: new BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)),
color: Colors.white,
boxShadow: [
BoxShadow(
blurRadius: 4,
color: Colors.black26,
offset: Offset(0, 4))
],
),
child: _buildDropListOptions(dropListModel.listOptionItems, context)
)
),
// Divider(color: Colors.grey.shade300, height: 1,)
],
),
);
}

Column _buildDropListOptions(List<OptionItem> items, BuildContext context) {
return Column(
children: items.map((item) => _buildSubMenu(item, context)).toList(),
);
}

Widget _buildSubMenu(OptionItem item, BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 26.0, top: 5, bottom: 5),
child: GestureDetector(
child: Row(
children: <Widget>[

child: Container(
padding: const EdgeInsets.only(top: 20),
decoration: BoxDecoration(
border: Border(top: BorderSide(color: Colors.grey[200], width: 1)),
),
child: Text(item.title,
style: TextStyle(
color: Color(0xFF307DF1),
fontWeight: FontWeight.w400,
fontSize: 14),
maxLines: 3,
textAlign: TextAlign.start,
overflow: TextOverflow.ellipsis),
),

],
),
onTap: () {
this.optionItemSelected = item;
isShow = false;
expandController.reverse();
widget.onOptionSelected(item);
},
),
);
}

}
初始化值:
DropListModel dropListModel = DropListModel([OptionItem(id: "1", title: "Option 1"), OptionItem(id: "2", title: "Option 2")]);
OptionItem optionItemSelected = OptionItem(id: null, title: "Chọn quyền truy cập");
最后使用它:
Container(height: 47, child: SelectDropList(
this.optionItemSelected,
this.dropListModel,
(optionItem){
optionItemSelected = optionItem;
setState(() {

});
},
))

最佳答案

按钮下方的自定义下拉菜单
我知道内置下拉菜单效果很好,但对于某些用例,我需要一些不同的东西。例如,如果我只有几个项目,我希望下拉菜单出现在按钮下方或完全控制下拉菜单的显示位置。我还没有找到一个好的选择,所以我试着自己做。我已经建立在@M123 在覆盖中提到的内容的基础上,并尝试以类似于内置下拉菜单的方式实现它。我从 flutter_typeahead 的开发者那里找到了这篇中等帖子。很有用。
https://medium.com/saugo360/https-medium-com-saugo360-flutter-using-overlay-to-display-floating-widgets-2e6d0e8decb9
animated gif of custom dropdown
该按钮使用覆盖创建全屏堆栈。这样我们就可以在下拉菜单后面添加一个全屏手势检测器,以便在用户点击屏幕上的任意位置时关闭它。
叠加层使用 LayerLink 链接到按钮和 CompositedTransformFollower小部件。
我们也使用 RenderBox renderBox = context.findRenderObject();轻松获取按钮的位置和大小。然后相应地定位下拉菜单。
下拉文件

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';

class CustomDropdown<T> extends StatefulWidget {
/// the child widget for the button, this will be ignored if text is supplied
final Widget child;

/// onChange is called when the selected option is changed.;
/// It will pass back the value and the index of the option.
final void Function(T, int) onChange;

/// list of DropdownItems
final List<DropdownItem<T>> items;
final DropdownStyle dropdownStyle;

/// dropdownButtonStyles passes styles to OutlineButton.styleFrom()
final DropdownButtonStyle dropdownButtonStyle;

/// dropdown button icon defaults to caret
final Icon icon;
final bool hideIcon;

/// if true the dropdown icon will as a leading icon, default to false
final bool leadingIcon;
CustomDropdown({
Key key,
this.hideIcon = false,
@required this.child,
@required this.items,
this.dropdownStyle = const DropdownStyle(),
this.dropdownButtonStyle = const DropdownButtonStyle(),
this.icon,
this.leadingIcon = false,
this.onChange,
}) : super(key: key);

@override
_CustomDropdownState<T> createState() => _CustomDropdownState<T>();
}

class _CustomDropdownState<T> extends State<CustomDropdown<T>>
with TickerProviderStateMixin {
final LayerLink _layerLink = LayerLink();
OverlayEntry _overlayEntry;
bool _isOpen = false;
int _currentIndex = -1;
AnimationController _animationController;
Animation<double> _expandAnimation;
Animation<double> _rotateAnimation;

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

_animationController =
AnimationController(vsync: this, duration: Duration(milliseconds: 200));
_expandAnimation = CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
);
_rotateAnimation = Tween(begin: 0.0, end: 0.5).animate(CurvedAnimation(
parent: _animationController,
curve: Curves.easeInOut,
));
}

@override
Widget build(BuildContext context) {
var style = widget.dropdownButtonStyle;
// link the overlay to the button
return CompositedTransformTarget(
link: this._layerLink,
child: Container(
width: style.width,
height: style.height,
child: OutlinedButton(
style: OutlinedButton.styleFrom(
padding: style.padding,
backgroundColor: style.backgroundColor,
elevation: style.elevation,
primary: style.primaryColor,
shape: style.shape,
),
onPressed: _toggleDropdown,
child: Row(
mainAxisAlignment:
style.mainAxisAlignment ?? MainAxisAlignment.center,
textDirection:
widget.leadingIcon ? TextDirection.rtl : TextDirection.ltr,
mainAxisSize: MainAxisSize.min,
children: [
if (_currentIndex == -1) ...[
widget.child,
] else ...[
widget.items[_currentIndex],
],
if (!widget.hideIcon)
RotationTransition(
turns: _rotateAnimation,
child: widget.icon ?? Icon(FontAwesomeIcons.caretDown),
),
],
),
),
),
);
}

OverlayEntry _createOverlayEntry() {
// find the size and position of the current widget
RenderBox renderBox = context.findRenderObject();
var size = renderBox.size;

var offset = renderBox.localToGlobal(Offset.zero);
var topOffset = offset.dy + size.height + 5;
return OverlayEntry(
// full screen GestureDetector to register when a
// user has clicked away from the dropdown
builder: (context) => GestureDetector(
onTap: () => _toggleDropdown(close: true),
behavior: HitTestBehavior.translucent,
// full screen container to register taps anywhere and close drop down
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
child: Stack(
children: [
Positioned(
left: offset.dx,
top: topOffset,
width: widget.dropdownStyle.width ?? size.width,
child: CompositedTransformFollower(
offset:
widget.dropdownStyle.offset ?? Offset(0, size.height + 5),
link: this._layerLink,
showWhenUnlinked: false,
child: Material(
elevation: widget.dropdownStyle.elevation ?? 0,
borderRadius:
widget.dropdownStyle.borderRadius ?? BorderRadius.zero,
color: widget.dropdownStyle.color,
child: SizeTransition(
axisAlignment: 1,
sizeFactor: _expandAnimation,
child: ConstrainedBox(
constraints: widget.dropdownStyle.constraints ??
BoxConstraints(
maxHeight: MediaQuery.of(context).size.height -
topOffset -
15,
),
child: ListView(
padding:
widget.dropdownStyle.padding ?? EdgeInsets.zero,
shrinkWrap: true,
children: widget.items.asMap().entries.map((item) {
return InkWell(
onTap: () {
setState(() => _currentIndex = item.key);
widget.onChange(item.value.value, item.key);
_toggleDropdown();
},
child: item.value,
);
}).toList(),
),
),
),
),
),
),
],
),
),
),
);
}

void _toggleDropdown({bool close = false}) async {
if (_isOpen || close) {
await _animationController.reverse();
this._overlayEntry.remove();
setState(() {
_isOpen = false;
});
} else {
this._overlayEntry = this._createOverlayEntry();
Overlay.of(context).insert(this._overlayEntry);
setState(() => _isOpen = true);
_animationController.forward();
}
}
}

/// DropdownItem is just a wrapper for each child in the dropdown list.\n
/// It holds the value of the item.
class DropdownItem<T> extends StatelessWidget {
final T value;
final Widget child;

const DropdownItem({Key key, this.value, this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return child;
}
}

class DropdownButtonStyle {
final MainAxisAlignment mainAxisAlignment;
final ShapeBorder shape;
final double elevation;
final Color backgroundColor;
final EdgeInsets padding;
final BoxConstraints constraints;
final double width;
final double height;
final Color primaryColor;
const DropdownButtonStyle({
this.mainAxisAlignment,
this.backgroundColor,
this.primaryColor,
this.constraints,
this.height,
this.width,
this.elevation,
this.padding,
this.shape,
});
}

class DropdownStyle {
final BorderRadius borderRadius;
final double elevation;
final Color color;
final EdgeInsets padding;
final BoxConstraints constraints;

/// position of the top left of the dropdown relative to the top left of the button
final Offset offset;

///button width must be set for this to take effect
final double width;

const DropdownStyle({
this.constraints,
this.offset,
this.width,
this.elevation,
this.color,
this.padding,
this.borderRadius,
});
}
使用下拉菜单
我尝试使用类似于内置下拉菜单的自定义下拉菜单,并增加了能够设置实际下拉菜单和按钮样式的额外好处。
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: CustomDropdown<int>(
child: Text(
'dropdown',
),
onChange: (int value, int index) => print(value),
dropdownButtonStyle: DropdownButtonStyle(
width: 170,
height: 40,
elevation: 1,
backgroundColor: Colors.white,
primaryColor: Colors.black87,
),
dropdownStyle: DropdownStyle(
borderRadius: BorderRadius.circular(8),
elevation: 6,
padding: EdgeInsets.all(5),
),
items: [
'item 1',
'item 2',
'item 3',
'item 4',
]
.asMap()
.entries
.map(
(item) => DropdownItem<int>(
value: item.key + 1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(item.value),
),
),
)
.toList(),
),
),
);
}

我相信那里会有一些需要改进的地方。但目前它对我有用。

关于flutter - 在 flutter 中创建自定义下拉列表 - 或者如何将自定义下拉选项放在其他所有内容之上的图层中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65685532/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com