Flutter 下拉选择器(包含二级联动)

3,834 阅读4分钟

直接上图了

因为是刚接触Flutter不久,所以里面有很多嵌套不是很合理,还有几个地方其实可以抽出来公用一个,需要的话可以自行优化。这里主要看具体功能就好了

智能排序

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

class DropDownMenuRouteLayout extends SingleChildLayoutDelegate {
  final Rect position;
  final double menuHeight;

  DropDownMenuRouteLayout({this.position, this.menuHeight});

  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    // TODO: implement getConstraintsForChild
    return BoxConstraints.loose(Size(position.right - position.left, 150));
  }

  @override
  Offset getPositionForChild(Size size, Size childSize) {
    // TODO: implement getPositionForChild
    return Offset(0, position.bottom);
  }

  @override
  bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
    // TODO: implement shouldRelayout
    return true;
  }
}

class DropDownMenuRoute extends PopupRoute {
  final Rect position;
  final double menuHeight;
  Function(int) rightClick;
  int pos = 0;

  DropDownMenuRoute({this.position, this.menuHeight,this.pos,this.rightClick});

  @override
  // TODO: implement barrierColor
  Color get barrierColor => null;

  @override
  // TODO: implement barrierDismissible
  bool get barrierDismissible => true;

  @override
  // TODO: implement barrierLabel
  String get barrierLabel => null;

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    final size = MediaQuery.of(context).size;

    // TODO: implement buildPage
    return CustomSingleChildLayout(
      delegate:
          DropDownMenuRouteLayout(position: position, menuHeight: menuHeight),
      child: SizeTransition(
        sizeFactor: Tween<double>(begin: 0.0, end: 1.0).animate(animation),
        child: Container(
          color: Colors.white,
          width: size.width,
          alignment: Alignment.centerLeft,
          child: Column(
            children: [
              GestureDetector(
                  onTap: () {
                    setState(() {
                      pos = 0;
                      Navigator.of(context).pop();
                      rightClick(0);
                    });
                  },
                  child: Container(
                    padding: EdgeInsets.only(top: 20, left: 20),
                    child: Text(
                      '智能排序',
                      style: TextStyle(
                          fontSize: 14,
                          color:
                              pos == 0 ? Color(0xff00D28C) : Color(0xff9A9A9A),
                          decoration: TextDecoration.none,
                          fontWeight: FontWeight.w400),
                    ),
                  )),
              GestureDetector(
                  onTap: () {
                    setState(() {
                      pos = 1;
                      Navigator.of(context).pop();
                      rightClick(1);
                    });
                  },
                  child: Container(
                    padding: EdgeInsets.only(top: 20, left: 20),
                    child: Text(
                      '离我最近',
                      style: TextStyle(
                          fontSize: 14,
                          color:
                              pos == 1 ? Color(0xff00D28C) : Color(0xff9A9A9A),
                          decoration: TextDecoration.none,
                          fontWeight: FontWeight.w400),
                    ),
                  )),
              GestureDetector(
                onTap: () {
                  setState(() {
                    Navigator.of(context).pop();
                    rightClick(2);
                  });
                },
                child: Container(
                  padding: EdgeInsets.only(top: 20, left: 20),
                  child: Text(
                    '价格最低',
                    style: TextStyle(
                        fontSize: 14,
                        color: pos == 2 ? Color(0xff00D28C) : Color(0xff9A9A9A),
                        decoration: TextDecoration.none,
                        fontWeight: FontWeight.w400),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  @override
  // TODO: implement transitionDuration
  Duration get transitionDuration => Duration(milliseconds: 300);
}

位置区域

import 'dart:ui';

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

class PositionSite extends SingleChildLayoutDelegate {
  final Rect position;
  final double menuHeight;

  PositionSite({this.position, this.menuHeight});

  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    // TODO: implement getConstraintsForChild
    return BoxConstraints.loose(Size(position.right - position.left, 400));
  }

  @override
  Offset getPositionForChild(Size size, Size childSize) {
    // TODO: implement getPositionForChild
    return Offset(0, position.bottom);
  }

  @override
  bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
    // TODO: implement shouldRelayout
    return true;
  }
}

class PositionSiteDropDownMenuRoute extends PopupRoute {
  final Rect position;
  final double menuHeight;
  Function(int) rightClick;
  int pos = 0;

  PositionSiteDropDownMenuRoute(
      {this.position, this.menuHeight, this.pos, this.rightClick});

  @override
  // TODO: implement barrierColor
  Color get barrierColor => null;

  @override
  // TODO: implement barrierDismissible
  bool get barrierDismissible => true;

  @override
  // TODO: implement barrierLabel
  String get barrierLabel => null;

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,Animation<double> secondaryAnimation) {
    // TODO: implement buildPage
    return CustomSingleChildLayout(
      delegate: PositionSite(position: position, menuHeight: menuHeight),
      child: SizeTransition(
        sizeFactor: Tween<double>(begin: 0.0, end: 1.0).animate(animation),
        child: PopRoute(rightClick),
      ),
    );
  }

  @override
  Duration get transitionDuration => Duration(milliseconds: 300);
}

class PopRoute extends StatefulWidget{
  final Function(int) rightClick;
  PopRoute(this.rightClick);

  @override
  State<StatefulWidget> createState() {
    return new _PopRoute(rightClick);
  }
}

class _PopRoute extends State<PopRoute>{
  Function(int) rightClick;
  _PopRoute(this.rightClick);

  List<String> _datas = []; //一级分类集合
  List<String> articles = []; //二级分类集合
  int index = 0; //一级分类下标
  Color textColor = Color(0xff00D28C); //字体颜色

  @override
  Widget build(BuildContext context) {

    _datas.clear();
    _datas.add('附近');
    _datas.add('行政区域');

    articles.add('附近');
    articles.add('1Km');
    articles.add('2Km');
    articles.add('3Km');
    articles.add('4Km');
    articles.add('5Km');
    articles.add('6Km');

    final size = MediaQuery.of(context).size;
    return Container(
      width: size.width,
      alignment: Alignment.centerLeft,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          Expanded(
            flex: 2,
            child: Container(
              color: Colors.white,
              child: ListView.builder(
                itemCount: _datas.length,
                itemBuilder: (BuildContext context, int position) {
                  return GestureDetector(
                    child: Container(
                      alignment: Alignment.topLeft,
                      margin: EdgeInsets.only(left: 10),
                      padding: EdgeInsets.symmetric(
                          vertical: 10, horizontal: 10),
                      child: Text(
                        _datas[position],
                        style: TextStyle(
                            color: index == position
                                ? textColor
                                : Color(0xff9A9A9A),
                            fontWeight: index == position
                                ? FontWeight.w600
                                : FontWeight.w400,
                            fontSize: 16,
                            decoration: TextDecoration.none),
                      ),
                    ),
                    onTap: () {
                      setState(() {
                        index = position; //记录选中的下标
                      });
                    },
                  );
                },
              ),
            ),
          ),
          Expanded(
              flex: 5,
              child: Container(
                color: Colors.white,
                child: getChip(index),
              )),
        ],
      ),
    );
  }

  Widget getChip(int i) {
    articles.clear();
    if(index == 0){
      articles.add('附近');
      articles.add('6Km');
      articles.add('6Km');
      articles.add('3Km');
      articles.add('4Km');
      articles.add('5Km');
      articles.add('6Km');
    }else{
      articles.add('上海');
      articles.add('北京');
      articles.add('江苏');
    }

    return ListView(
      children: <Widget>[
        Container(
          alignment: Alignment.topLeft,
          padding: const EdgeInsets.all(10),
          child: Wrap(
            spacing: 20.0, //两个widget之间横向的间隔
            direction: Axis.vertical, //方向
            alignment: WrapAlignment.start, //内容排序方式
            children: List<Widget>.generate(
              articles.length,
                  (int index) {
                return GestureDetector(
                  onTap: () {
                    Navigator.of(context).pop();
                  },
                  child: Text(
                    articles[index],
                    style: TextStyle(
                        fontSize: 16,
                        color: Color(0xff9A9A9A),
                        decoration: TextDecoration.none,
                        fontWeight: FontWeight.w400),
                  ),
                );
              },
            ).toList(),
          ), //传入一级分类下标
        ),
      ],
    );
  }
}

综合筛选

import 'dart:ui';

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

class PositionSite extends SingleChildLayoutDelegate {
  final Rect position;
  final double menuHeight;

  PositionSite({this.position, this.menuHeight});

  @override
  BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
    // TODO: implement getConstraintsForChild
    return BoxConstraints.loose(Size(position.right - position.left, 400));
  }

  @override
  Offset getPositionForChild(Size size, Size childSize) {
    // TODO: implement getPositionForChild
    return Offset(0, position.bottom);
  }

  @override
  bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
    // TODO: implement shouldRelayout
    return true;
  }
}

class ComprehensiveSite extends PopupRoute {
  final Rect position;
  final double menuHeight;

  ComprehensiveSite({this.position, this.menuHeight});

  @override
  // TODO: implement barrierColor
  Color get barrierColor => null;

  @override
  // TODO: implement barrierDismissible
  bool get barrierDismissible => true;

  @override
  // TODO: implement barrierLabel
  String get barrierLabel => null;

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    // TODO: implement buildPage
    return CustomSingleChildLayout(
      delegate: PositionSite(position: position, menuHeight: menuHeight),
      child: SizeTransition(
        sizeFactor: Tween<double>(begin: 0.0, end: 1.0).animate(animation),
        child: PopRoute(),
      ),
    );
  }

  @override
  // TODO: implement transitionDuration
  Duration get transitionDuration => Duration(milliseconds: 300);
}

class PopRoute extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _PopRoute();
  }
}

class _PopRoute extends State<PopRoute> {
  //直流
  bool isSelectDc = false;

  //交流
  bool isSelectAc = false;

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.of(context).size;

    var minPower = TextEditingController();
    var maxPower = TextEditingController();

    minPower.text = '1';
    maxPower.text = '360';

    return Container(
      width: size.width,
      color: Colors.white,
      alignment: Alignment.centerLeft,
      child: Column(
        children: <Widget>[
          Container(
              width: size.width,
              margin: EdgeInsets.symmetric(vertical: 20, horizontal: 25),
              child: Text(
                '充电类型',
                style: TextStyle(
                    color: Color(0xff00D28C),
                    decoration: TextDecoration.none,
                    fontSize: 16,
                    fontWeight: FontWeight.w500),
              )),
          Container(
            width: size.width,
            child: Row(
              children: [
                Expanded(
                  flex: 1,
                  child: Container(
                    padding: EdgeInsets.only(left: 15, right: 10),
                    child: RaisedButton.icon(
                      onPressed: () {
                        setState(() {
                          isSelectDc ? isSelectDc = false : isSelectDc = true;
                        });
                      },
                      icon: Image.asset('images/icon_dc_nor.png'),
                      label: Text('直流'),
                      elevation: 1,
                      color:
                          !isSelectDc ? Color(0xffF4F4F4) : Color(0xff00D28C),
                    ),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Container(
                    padding: EdgeInsets.only(right: 15, left: 10),
                    child: RaisedButton.icon(
                      onPressed: () {
                        setState(() {
                          isSelectAc ? isSelectAc = false : isSelectAc = true;
                        });
                      },
                      icon: Image.asset('images/icon_ac_nor.png'),
                      label: Text('交流'),
                      color:
                          !isSelectAc ? Color(0xffF4F4F4) : Color(0xff00D28C),
                      elevation: 1,
                    ),
                  ),
                ),
              ],
            ),
          ),
          Container(
              width: size.width,
              margin: EdgeInsets.symmetric(vertical: 20, horizontal: 25),
              child: Text(
                '充电功率(kw)',
                style: TextStyle(
                    color: Color(0xff00D28C),
                    decoration: TextDecoration.none,
                    fontSize: 16,
                    fontWeight: FontWeight.w500),
              )),
          Container(
            width: size.width,
            child: Row(
              children: [
                Expanded(
                  flex: 1,
                  child: Container(
                    padding: EdgeInsets.only(left: 15, right: 10),
                    child: FlatButton(
                      onPressed: () {},
                      child: ConstrainedBox(
                        constraints: BoxConstraints(
                          maxHeight: 25,
                        ),
                        child: TextField(
                          textAlign: TextAlign.center,
                          controller: minPower,
                          decoration: InputDecoration(
                            border: InputBorder.none,
                          ),
                        ),
                      ),
                      color: Color(0xffF4F4F4),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(30)),
                    ),
                  ),
                ),
                Container(
                  child: Text(
                    '-',
                    style: TextStyle(
                        fontWeight: FontWeight.w100,
                        color: Colors.black,
                        decoration: TextDecoration.none),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Container(
                    padding: EdgeInsets.only(left: 15, right: 10),
                    child: FlatButton(
                      onPressed: () {},
                      child: ConstrainedBox(
                        constraints: BoxConstraints(
                          maxHeight: 25
                        ),
                        child: TextField(
                          textAlign: TextAlign.center,
                          controller: maxPower,
                          decoration: InputDecoration(
                            border: InputBorder.none,
                          ),
                        ),
                      ),
                      color: Color(0xffF4F4F4),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(30)),
                    ),
                  ),
                ),
              ],
            ),
          ),
          Container(
            padding: EdgeInsets.only(top: 100),
            child: Row(
              children: [
                Expanded(
                  flex: 1,
                  child: Container(
                    padding: EdgeInsets.only(left: 15, right: 10),
                    child: RaisedButton(
                      onPressed: () {
                        setState(() {
                          minPower.text = "1";
                          maxPower.text = "360";
                        });
                      },
                      child: Text('重置'),
                      elevation: 1,
                      color: Color(0xffF4F4F4),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(30)),
                    ),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Container(
                    padding: EdgeInsets.only(left: 15, right: 10),
                    child: RaisedButton(
                      onPressed: () {
                        Navigator.of(context).pop();
                      },
                      child: Text('应用'),
                      elevation: 1,
                      textColor: Color(0xffFFFFFF),
                      color: Color(0xff00D28C),
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(30)),
                    ),
                  ),
                ),
              ],
            ),
          )
        ],
      ),
    );
  }
}

具体使用(部分使用代码

                GestureDetector(
                    onTap: () {
                      RenderBox renderBox =
                          _globalKey.currentContext.findRenderObject();
                      Rect box =
                          renderBox.localToGlobal(Offset.zero) & renderBox.size;
                      Navigator.push(
                          context,
                          DropDownMenuRoute(
                              position: box,
                              menuHeight: 300,
                              pos: pos,
                              rightClick: (pos) {
                                this.pos = pos;
                              }));
                    },
                    child: Row(
                      children: <Widget>[Text('智能排序'), Icon(Icons.expand_more)],
                    ),
                  ),
                  GestureDetector(
                    onTap: () {
                      RenderBox renderBox =
                          _globalKey.currentContext.findRenderObject();
                      Rect box =
                          renderBox.localToGlobal(Offset.zero) & renderBox.size;
                      Navigator.push(
                          context,
                          PositionSiteDropDownMenuRoute(
                              position: box,
                              menuHeight: 300,
                              pos: pos,
                              rightClick: (pos) {}));
                    },
                    child: Container(
                      child: Row(
                        children: <Widget>[
                          Text('位置区域'),
                          Icon(Icons.expand_more)
                        ],
                      ),
                    ),
                  ),
                  GestureDetector(
                    onTap: () {
                      RenderBox renderBox =
                          _globalKey.currentContext.findRenderObject();
                      Rect box =
                          renderBox.localToGlobal(Offset.zero) & renderBox.size;
                      Navigator.push(
                          context,
                          ComprehensiveSite(
                            position: box,
                            menuHeight: 300,
                          ));
                    },
                    child: Row(
                      children: <Widget>[Text('综合筛选'), Icon(Icons.expand_more)],
                    ),
                  )