玖叶教程网

前端编程开发入门

Flutter 优雅的实现一个下拉框的效果

#文章首发挑战赛#

效果预览

首先是需要添加一个依赖如下:

flutter_portal: ^1.1.4

可以访问插件库,查看当下最新的版本

https://pub.dev/packages/flutter_portal

本文章开发使用的Flutter版本为 3.16.0

Flutter 项目,首先是程序入口如下:

import 'package:flutter/material.dart';
import 'package:flutter_portal/flutter_portal.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Portal(
      child: MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            title: const Text('下拉选择框使用案例'),
          ),
          body: const Padding(
            padding: EdgeInsets.all(16),
            child: RoundedCornersWidget(),
          ),
        ),
      ),
    );
  }
}

RoundedCornersWidget 是用来组合封装的各种小组件的,代码如下:

class RoundedCornersWidget extends StatefulWidget {
  const RoundedCornersWidget({Key? key}) : super(key: key);

  @override
  State createState() => _RoundedCornersWidgetState();
}

class _RoundedCornersWidgetState extends State<RoundedCornersWidget> {
  bool _showPopup = false;
  @override
  Widget build(BuildContext context) {
    return _ModalEntry(
      visible: _showPopup,//true显示下拉框 false 隐藏
      onClose: () => setState(() => _showPopup = false),//关闭回调
      popup: _Popup( //显示的弹框的内容
        children: [
          //弹框中显示的列表内容
          for (var i = 0; i < 4; i++)
            ListTile(
              onTap: () => setState(() => _showPopup = false),
              title: Text('$i'),
            ),
        ],
      ),
      child: ElevatedButton(
        onPressed: () => setState(() => _showPopup = true),
        child: const Text('show popup'),
      ),
    );
  }
}

我这实现的就是点击按钮来弹出来一个下拉,本质上还是使用了Flutter 的OverlayPortal,所以 自定义的 _ModalEntry 来组合按钮还有弹框的内容。

lass _ModalEntry extends StatelessWidget {
  const _ModalEntry({
    Key? key,
    required this.onClose,
    required this.visible,
    required this.popup,
    required this.child,
  }) : super(key: key);

  final VoidCallback onClose;
  final bool visible;
  final Widget popup;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onTap: visible ? onClose : null,
      child: PortalTarget(
        visible: visible,
        portalFollower: popup,
        anchor: const Aligned(
          follower: Alignment.topLeft,
          target: Alignment.bottomLeft,
          widthFactor: 1,
        ),
        child: IgnorePointer(
          ignoring: visible,
          child: child,
        ),
      ),
    );
  }
}

_Popup 相当于是实现的封闭功能

class _Popup extends StatelessWidget {
  const _Popup({
    Key? key,
    required this.children,
  }) : super(key: key);

  final List<Widget> children;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(
        bottom: 16,
      ),
      child: Card(
        elevation: 8,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(8),
        ),
        child: IntrinsicWidth(
          child: ListView(
            shrinkWrap: true,
            children: children,
          ),
        ),
      ),
    );
  }
}
  • Padding 用来实现这里使用到的内边距
  • Card 用来实现圆角的卡片效果,其中 elevation 是阴影高度,shape 用来设置卡片圆角大小
  • IntrinsicWidth 使内容宽度适配到最小的值
  • ListView 就是用来具体加载列表的,shrinkWrap设置为true,代表一次性渲染出所有的Item

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言