flutter 下拉篩選組件

效果圖

QQ錄屏20211019151055202110191512221.gif

網(wǎng)頁版(第一次打開網(wǎng)址加載會很慢)
網(wǎng)頁版

代碼

import 'dart:ui';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter1/widget/drop_down_widget.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

import 'base/base_routes_widget.dart';

/// @description 作用:篩選組件演示
/// @date: 2021/10/14
/// @author:盧融霜
class DropDownPage extends StatefulWidget {
  const DropDownPage({Key key}) : super(key: key);

  @override
  _DropDownPageState createState() => _DropDownPageState();
}

class _DropDownPageState extends State<DropDownPage> {
  List<String> titles = ["北京", "招標(biāo)類型", "更多篩選"];
  List<Widget> listWidget = [];
  ScreenControl screenControl = ScreenControl();
  List<String> types = ["全部", "招標(biāo)", "中標(biāo)", "公示"];

  @override
  void initState() {
    listWidget = [
      Row(
        children: [
          Expanded(
              flex: 1,
              child: ListView.builder(itemBuilder: (context, index) {
                return InkWell(
                  onTap: () {},
                  child: Container(
                    decoration: const BoxDecoration(
                        border: Border(
                            bottom: BorderSide(color: Color(0xffdddddd)))),
                    padding: EdgeInsets.all(10.r),
                    child: Text("北京", style: TextStyle(fontSize: 14.sp)),
                  ),
                );
              })),
          Expanded(
              flex: 1,
              child: ListView.builder(itemBuilder: (context, index) {
                return InkWell(
                  onTap: () {
                    titles[0] = "省份";
                    screenControl.screenHide();
                  },
                  child: Container(
                    decoration: const BoxDecoration(
                        color: Color(0xffdddddd),
                        border: Border(
                            bottom: BorderSide(color: Color(0xffffffff)))),
                    padding: EdgeInsets.all(10.r),
                    child: Text("北京", style: TextStyle(fontSize: 14.sp)),
                  ),
                );
              }))
        ],
      ),
      ListView.builder(
          shrinkWrap: true,
          itemCount: types.length,
          itemBuilder: (context, index) {
            return Container(
              width: double.infinity,
              decoration: const BoxDecoration(
                  border: Border(bottom: BorderSide(color: Color(0xffdddddd)))),
              padding: EdgeInsets.all(10.r),
              child: InkWell(
                  onTap: () {
                    titles[1] = types[index];
                    screenControl.screenHide();
                  },
                  child: Text(
                    types[index],
                    style: TextStyle(fontSize: 14.sp),
                  )),
            );
          }),
      ListView(
        shrinkWrap: true,
        children: [
          Container(
            padding: EdgeInsets.only(
                left: 15.r, top: 10.r, right: 15.r, bottom: 10.r),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text("檢索內(nèi)容",
                    style:
                        TextStyle(fontSize: 14.r, fontWeight: FontWeight.bold)),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    MaterialButton(
                      onPressed: () {},
                      color: Colors.blue,
                      textColor: Colors.white,
                      child: Text(
                        "全部",
                        style: TextStyle(fontSize: 14.r),
                      ),
                    ),
                    MaterialButton(
                      onPressed: () {},
                      color: Colors.blue,
                      textColor: Colors.white,
                      child: Text(
                        "標(biāo)題",
                        style: TextStyle(fontSize: 14.r),
                      ),
                    ),
                    MaterialButton(
                      onPressed: () {},
                      color: Colors.blue,
                      textColor: Colors.white,
                      child: Text(
                        "內(nèi)容",
                        style: TextStyle(fontSize: 14.r),
                      ),
                    )
                  ],
                )
              ],
            ),
          ),
          Container(
            padding: EdgeInsets.only(left: 15.r, right: 15.r, bottom: 10.r),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text("匹配方式",
                    style:
                        TextStyle(fontSize: 14.r, fontWeight: FontWeight.bold)),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    MaterialButton(
                      onPressed: () {},
                      color: Colors.blue,
                      textColor: Colors.white,
                      child: Text(
                        "全部",
                        style: TextStyle(fontSize: 14.r),
                      ),
                    ),
                    MaterialButton(
                      onPressed: () {},
                      color: Colors.blue,
                      textColor: Colors.white,
                      child: Text(
                        "模糊",
                        style: TextStyle(fontSize: 14.r),
                      ),
                    ),
                    MaterialButton(
                      onPressed: () {},
                      color: Colors.blue,
                      textColor: Colors.white,
                      child: Text(
                        "精準(zhǔn)",
                        style: TextStyle(fontSize: 14.r),
                      ),
                    )
                  ],
                )
              ],
            ),
          )
        ],
      )
    ];
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return BaseRoutesWidget(
        title: '篩選組件',
        child: DropDownWidget(
          titles,
          listWidget,
          height: 40.r,
          bottomHeight: 400.r,
          screenControl: screenControl,
          child: Container(
            margin: EdgeInsets.only(top: 40.4.r),
            child: ListView.builder(itemBuilder: (context, index) {
              return Container(
                padding: EdgeInsets.all(15.r),
                child: Text("item $index"),
                decoration: BoxDecoration(
                    border: Border(
                        bottom: BorderSide(
                            color: const Color(0xffeeeeee), width: 0.4.r))),
              );
            }),
          ),
        ));
  }
}

篩選組件

import 'dart:ui';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter1/widget/drop_down_head_widget.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

/// @description 作用:多級篩選
/// @date: 2021/10/14
/// @author:盧融霜
class DropDownWidget extends StatefulWidget {
  //標(biāo)題集合
  List<String> titles;

  //展開視圖集合
  List<Widget> listWidget;

  // 高度
  double height;

  //子集
  Widget child;

  //篩選文字大小
  double headFontSize;

  // 篩選圖標(biāo)icons
  IconData iconData;

  //篩選高度 限制
  // BoxConstraints constraints;
  double bottomHeight;

  ScreenControl screenControl;

  DropDownWidget(this.titles, this.listWidget,
      {this.child,
      this.height = 42,
      this.headFontSize,
      this.iconData,
      this.bottomHeight,
      this.screenControl,
      Key key})
      : super(key: key);

  @override
  _DropDownWidgetState createState() => _DropDownWidgetState();
}

AnimationController _controller;
Animation<double> curve;
//按鈕旋轉(zhuǎn)狀態(tài)
List<bool> rotateState = [];

class _DropDownWidgetState extends State<DropDownWidget>
    with SingleTickerProviderStateMixin {
  int tabIndex = 0;
  final ScreenControl _screenControl = ScreenControl();
  bool showBottom = false;

  @override
  void initState() {
    super.initState();
    widget.bottomHeight ??= 400.r;
    //展開隱藏控制器巢钓,動畫初始化
    _controller = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 200));
    curve = CurvedAnimation(parent: _controller, curve: Curves.decelerate);
    curve = Tween(begin: 0.0, end: widget.bottomHeight).animate(curve)
      ..addListener(() {
        setState(() {
          if (curve.value > 0) {
            showBottom = true;
          } else {
            showBottom = false;
          }
        });
      });
    rotateState = [];
    widget.titles.toList().forEach((element) {
      rotateState.add(false);
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Container(
          alignment: Alignment.center,
          height: widget.height,
          decoration: BoxDecoration(
              border: Border(
                  bottom: BorderSide(
                      color: const Color(0xffe4e7ed), width: 0.4.r))),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: getScreenTitle(),
          ),
        ),
        widget.child,
        getBottomScreen()
      ],
    );
  }

  List<Widget> getScreenTitle() {
    List<Widget> widgets = [];
    if (widget.titles.isNotEmpty) {
      for (int i = 0; i < widget.titles.length; i++) {
        widgets.add(Expanded(
            flex: 1,
            child: DropDownHeadWidget(
              widget.titles[i],
              getRoState(i),
              () {
                print("click${rotateState.length}");
                setState(() {
                  tabIndex = i;
                  for (int j = 0; j < rotateState.length; j++) {
                    if (i == j) {
                      if (rotateState[j]) {
                        rotateState = rotateState.map((e) => false).toList();
                        _controller.reverse();
                      } else {
                        rotateState = rotateState.map((e) => false).toList();
                        rotateState[j] = !rotateState[j];
                        _controller.forward();
                      }
                    }
                  }
                });
              },
              headFontSize: widget.headFontSize,
              iconData: widget.iconData,
            )));
      }
    } else {
      widgets.add(Text(
        "數(shù)組為空",
        style: TextStyle(fontSize: 14.sp),
      ));
    }
    return widgets;
  }

  Widget getBottomScreen() {
    return SizedBox(
      height: showBottom ? double.infinity : 0.0,
      child: GestureDetector(
        onTap: () {
          _screenControl.screenHide();
        },
        child: getBottomIndex(),
      ),
    );
  }

  bool getRoState(int i) {
    if (rotateState.isEmpty || rotateState.length < i + 1) {
      return false;
    }
    return rotateState[i];
  }

  Widget getBottomIndex() {
    widget.bottomHeight ??= 400.r;

    return Container(
      margin: EdgeInsets.only(top: widget.height),
      alignment: Alignment.topCenter,
      color: Colors.black26,
      height: MediaQuery.of(context).size.height - widget.height,
      width: double.infinity,
      child: Container(
        width: double.infinity,
        constraints: BoxConstraints(maxHeight: curve.value),
        color: Colors.white,
        child: widget.listWidget[tabIndex],
      ),
    );
  }
}

class ScreenControl {
  //自動
  void autoDisplay() {
    if (_controller.isDismissed) {
      _controller.forward();
    } else {
      _controller.reverse();
      rotateState = rotateState.map((e) => false).toList();
    }
  }

  //顯示
  void screenShow() {
    _controller.forward();
  }

  //隱藏
  void screenHide() {
    _controller.reverse();
    rotateState = rotateState.map((e) => false).toList();
  }
}

篩選頭部組件

import 'dart:ui';

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

/// @description 作用:多級篩選item
/// @date: 2021/10/14
/// @author:盧融霜
class DropDownHeadWidget extends StatefulWidget {
  String title;
  bool isForward;
  Function onClick;
  double headFontSize;
  IconData iconData;

  DropDownHeadWidget(this.title, this.isForward, this.onClick,
      {this.headFontSize = 12.0, this.iconData = Icons.arrow_drop_down_outlined, key})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _DropDownHeadWidgetState();
  }
}

class _DropDownHeadWidgetState extends State<DropDownHeadWidget>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation shAnimation;

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

  @override
  void didUpdateWidget(covariant DropDownHeadWidget oldWidget) {
    if (widget.isForward) {
      controller.forward();
    } else {
      controller.reverse();
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  void initState() {
    controller = AnimationController(
        duration: const Duration(milliseconds: 200), vsync: this);
    shAnimation = Tween(begin: .0, end: .5).animate(controller)
      ..addStatusListener((status) {
        setState(() {
          if (status == AnimationStatus.completed) {
          } else {}
        });
      });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    TextStyle headTextStyle;
    Icon iconData;
    if (shAnimation.isCompleted) {
      headTextStyle ??=
          TextStyle(fontSize: widget.headFontSize, color: Colors.blue);
      iconData = Icon(

        widget.iconData??Icons.arrow_drop_down_sharp,
        color: Colors.blue,
        size: 20.sp,
      );
      //
    } else {
      headTextStyle = TextStyle(
          fontSize: widget.headFontSize, color: const Color(0xff333333));
      iconData = Icon(
        widget.iconData??Icons.arrow_drop_down_sharp,
        size: 20.sp,
        color: const Color(0xff333333),
      );
    }
    return InkWell(
      onTap: () {
        widget.onClick();
      },
      child: Container(
          alignment: Alignment.center,
          width: double.infinity,
          height: double.infinity,
          padding: EdgeInsets.only(left: 5.r, right: 5.r),
          child: Row(mainAxisAlignment: MainAxisAlignment.center, children: [
            Text(widget.title, style: headTextStyle),
            RotationTransition(
              turns: shAnimation,
              child: iconData,
            )
          ])),
    );
  }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末玲昧,一起剝皮案震驚了整個濱河市摩瞎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌纹份,老刑警劉巖苟跪,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蔓涧,居然都是意外死亡件已,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門元暴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篷扩,“玉大人,你說我怎么就攤上這事茉盏≌巴铮” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵援岩,是天一觀的道長。 經(jīng)常有香客問我掏导,道長享怀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任趟咆,我火速辦了婚禮添瓷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘值纱。我一直安慰自己鳞贷,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布虐唠。 她就那樣靜靜地躺著搀愧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪疆偿。 梳的紋絲不亂的頭發(fā)上咱筛,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機(jī)與錄音杆故,去河邊找鬼迅箩。 笑死,一個胖子當(dāng)著我的面吹牛处铛,可吹牛的內(nèi)容都是我干的饲趋。 我是一名探鬼主播拐揭,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奕塑!你這毒婦竟也來了堂污?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤爵川,失蹤者是張志新(化名)和其女友劉穎敷鸦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體寝贡,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡扒披,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了圃泡。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碟案。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖颇蜡,靈堂內(nèi)的尸體忽然破棺而出价说,到底是詐尸還是另有隱情,我是刑警寧澤风秤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布鳖目,位于F島的核電站,受9級特大地震影響缤弦,放射性物質(zhì)發(fā)生泄漏领迈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一碍沐、第九天 我趴在偏房一處隱蔽的房頂上張望狸捅。 院中可真熱鬧,春花似錦累提、人聲如沸尘喝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽朽褪。三九已至,卻和暖如春无虚,著一層夾襖步出監(jiān)牢的瞬間鞍匾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工骑科, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留橡淑,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓咆爽,卻偏偏與公主長得像梁棠,于是被迫代替她去往敵國和親置森。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容