flutter listview實現(xiàn)滑動刪除——方式二

上一篇介紹了實現(xiàn)flutter listview實現(xiàn)滑動刪除的方式一叹洲,但是很多時候這種方式跟需求不太一致,下面將介紹第二種方式:實現(xiàn)帶刪除按鈕的滑動刪除惕橙。

首先還是先看一下實現(xiàn)效果:


view.gif

那這種效果就比較符合需求了肮韧,具體實現(xiàn)看下面代碼

首先是每一個item的邏輯處理,在看代碼前先分析一下:
1.從動圖中可以想到的布局方式應(yīng)該是stack+position然走,讓具體內(nèi)容遮蓋住要滑動顯示的內(nèi)容即可。
2.從動圖可以看到我們要處理的是item的水平滑動的手勢操作戏挡,當(dāng)滑動超過一定距離的時候讓item自動打開芍瑞。當(dāng)關(guān)閉的時候也是滑動回一定距離的時候自動關(guān)閉。
3.滑動某項的時候關(guān)閉已經(jīng)打開的item褐墅。
4.點擊刪除和修改按鈕時外部實現(xiàn)具體操作拆檬。

好了根據(jù)以上分析洪己,先直接把item的代碼寫上,等下在做具體分析

typedef ClickDelete =Function(int position);//定義刪除方法
typedef ClickChange =Function(int position);//定義修改方法


class RemoveItem extends StatefulWidget  {
  final Result result;//數(shù)據(jù)bean
  final GlobalKey<RemoveItemState> moveKey;
  final VoidCallback onStart;//開始滑動回調(diào)
  final ClickDelete delete;
  final ClickDelete change;

  final int position;//操作position

  final Widget child;//具體顯示內(nèi)容

  RemoveItem(this.result,this.position,this.child,{@required this.moveKey,this.onStart,this.delete,this.change}):super(key:moveKey);
  @override
  RemoveItemState createState() => RemoveItemState();
}

class RemoveItemState extends State<RemoveItem> with SingleTickerProviderStateMixin{


 // Animation<double> animation;
  AnimationController controller;

  double moveMaxLength=160;//滑動最大距離
  double start=0;

  bool isOpen=false;//是否是打開狀態(tài)


  @override
  void initState() {
    super.initState();
//初始化動畫竟贯,讓item可以實現(xiàn)自動滑動
    controller = new AnimationController( lowerBound: 0,
        upperBound: moveMaxLength,duration: const Duration(milliseconds: 300), vsync: this)
      ..addListener((){
        start=controller.value;
        setState(() {});
      });

  }

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

  @override
  Widget build(BuildContext context) {
    return Container(height:115,padding: EdgeInsets.only(left: 15,right: 15,top: 15),width:MediaQuery.of(context).size.width,child:GestureDetector(
      child:Stack(children: <Widget>[
        Positioned(right: 80,child:InkWell(onTap: (){widget.change(widget.position); },child:Container(width: 80,height:100,alignment: Alignment.center,color: Colors.grey, child: Text("修改",style: TextStyle(color: Colors.white),),),),),
        Align(alignment: Alignment.centerRight,child: InkWell(onTap: (){widget.delete(widget.position); },child: Container(width: 80,alignment: Alignment.center,color: Colors.red,child: Text("刪除",style: TextStyle(color: Colors.white)),),),),
        Positioned(left: -start,right:start,child: widget.child),

      ],),onHorizontalDragDown: (DragDownDetails details){//滑動開始的時候關(guān)閉打開的item
      close();
      return widget.onStart();
    },
      onHorizontalDragUpdate: (DragUpdateDetails details){//滑動中更新滑動距離
        setState(() {
          start-=details.delta.dx;
          if (start<=0) {//限制最小滑動距離
            start=0;
          }

          if(start>=moveMaxLength){//限制最大滑動距離
            start=moveMaxLength;
          }
        });
      },onHorizontalDragEnd: (DragEndDetails details){
      controller.value=start;//滑動結(jié)束的時候給動畫value賦值為當(dāng)前值
      if (start==moveMaxLength) isOpen=true;//滑動距離最大的時候即為打開狀態(tài)
      else if (start>moveMaxLength/2) {//滑動超過一般距離的時候答捕,啟動動畫滑動到最大位置
        controller.animateTo(moveMaxLength);
        isOpen=true;
      }else if(start<=moveMaxLength/2){//往回滑動超過一般距離的時候,啟動動畫滑動到初始位置
        close();
      }

    },));
  }

  void close(){
    controller.animateTo(0);
    isOpen=false;

  }


}

現(xiàn)在一一實現(xiàn)上面分析時說到的4點屑那。

一.布局:

布局上有三點分別是詳細(xì)內(nèi)容(需要滑動的item)拱镐、修改和刪除按鈕。這里用的布局方式是stack+position持际。首先是讓要滑動的內(nèi)容占滿item沃琅,然后用position將修改和刪除依次定位在右側(cè)。因為滑動內(nèi)容要跟隨手指移動选酗,所以需要動態(tài)改變其左右距離阵难。根據(jù)上面分析做出了如下布局

 child:Stack(children: <Widget>[
        Positioned(right: 80,child:InkWell(onTap: (){widget.change(widget.position); },child:Container(width: 80,height:100,alignment: Alignment.center,color: Colors.grey, child: Text("修改",style: TextStyle(color: Colors.white),),),),),
        Align(alignment: Alignment.centerRight,child: InkWell(onTap: (){widget.delete(widget.position); },child: Container(width: 80,alignment: Alignment.center,color: Colors.red,child: Text("刪除",style: TextStyle(color: Colors.white)),),),),
        Positioned(left: -start,right:start,child: widget.child),//滑動內(nèi)容  start動態(tài)改變

      ],)

這里有一點要注意的是這句代碼:
Positioned(left: -start,right:start,child: widget.child),//滑動內(nèi)容 start動態(tài)改變
即左右都要設(shè)置start,那如果只設(shè)置了right芒填,left設(shè)置為0是什么效果呢,看下面的動圖

view3.gif

好像沒什么區(qū)別空繁,也能實現(xiàn)效果殿衰,但仔細(xì)觀察一下就會發(fā)現(xiàn)移動的時候藍色區(qū)域明顯只是距離右邊的距離在改變,而不是整體移動的效果盛泡。
單一.png

整體.png

二.自動開關(guān):

在Android中可以利用Scroller實現(xiàn)闷祥,但在flutter中我沒找到Scroller,因此用動畫代替傲诵。首先是在initState方法中初始化動畫AnimationController


初始化動畫之后先放著等會用凯砍,在水平手勢按下的時候,先把自身關(guān)閉掉同時回調(diào)一個onstart方法供外部調(diào)用拴竹,那這個作用等會再說悟衩。接著在手勢更新的時候改變start的值并調(diào)用setState,代碼中做了最大值和最小值的判斷栓拜,防止滑動超出限制座泳,之后就是在水平手勢結(jié)束的時候先把start值賦給動畫,讓動畫從當(dāng)前開始幕与。如果已經(jīng)移動了最大值那就不用開啟動畫了挑势,如果沒有的話判斷已經(jīng)滑動超過一半距離的時候,啟動動畫滑動到最大位置啦鸣,否則滑動到初始位置


3.png

三.滑動某項的時候關(guān)閉已經(jīng)打開的item

這個操作就需要在外部進行控制了潮饱,在Android中要進行控制是很方便的,在flutter中就有點惡心了诫给。好在flutter提供了key香拉,至于key是干嘛的就以后在分析了啦扬。通過給每一個item設(shè)置GlobalKey,并在外部通過GlobalKey調(diào)用關(guān)閉方法就可以做到了缕溉,具體實現(xiàn)可以查看代碼考传。

四.點擊刪除和修改按鈕時外部實現(xiàn)具體操作

這里我先定義了兩個方法,通過方法傳遞position來告知外部點擊的是哪個position证鸥,然后做具體操作僚楞。

typedef ClickDelete =Function(int position);//定義刪除方法
typedef ClickChange =Function(int position);//定義修改方法

然后點擊刪除或者修改按鈕的時候調(diào)用相應(yīng)方法即可。

到這里item的邏輯就分析完了枉层,接著看怎么調(diào)用item并實現(xiàn)相應(yīng)操作泉褐,同樣的先看一下完整代碼

class ListRemovePage extends StatefulWidget {
  @override
  _ListRemovePageState createState() => _ListRemovePageState();
}

class _ListRemovePageState extends State<ListRemovePage> {


  List<Result> listBank=new List();
  int positionNow=0;

  List<GlobalKey<RemoveItemState>> listKey=[];//通過給各個item設(shè)置key,點擊其它item的時候鸟蜡,打開的item關(guān)閉

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

  void initList(){
    listBank=List.generate(10, (index){
      Result result=new Result("title $index","detail $index");
      return result;
    });
    updateView(listBank);
  }



  void updateView(List<Result> list){
    listKey.clear();
    listKey.addAll(setKey(list.length));
    setState(() {});
  }

  List<GlobalKey<RemoveItemState>> setKey(int length){
    var list=<GlobalKey<RemoveItemState>>[];
    for (int i = 0; i < length; i++) {
      var key=GlobalKey<RemoveItemState>();
      list.add(key);
    }
    return list;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: Text("List滑動刪除"),centerTitle: true,elevation:0,),
      body:ListView.builder(itemCount:listBank.length,itemBuilder: (BuildContext context, int index){
        return RemoveItem(listBank[index],index,new RemoveWidget(listBank[index]),moveKey: listKey[index],onStart:(){//1.設(shè)置movekey
          listKey.forEach((bankKey){//2.循環(huán)關(guān)閉其他item
            if (bankKey!=listKey[index]) {
              bankKey.currentState?.close();
            }
          });
        },delete: (position){
          positionNow=position;
          showLoginDialog();
        },change: (position){
          Toast.toast(context,msg: "你點擊了修改 $position");
        },);
      }),
    );
  }


  void showLoginDialog() {

    showModalBottomSheet(context: context, builder: (context){
      return Container(height: 170,color: Colors.white,child: Column(children: <Widget>[
        SizedBox(height: 20,),
        Text("刪除后將無法看到該條記錄膜赃,請謹(jǐn)慎操作",style: TextStyle(color: Colors.grey,fontSize: 14),),
        SizedBox(height: 1,),
        Container(height: 50,width:double.infinity,margin:EdgeInsets.only(left: 15,right: 15),
          child:  FlatButton(onPressed:(){
            Navigator.of(context).pop();
            _deleteBank();
            Toast.toast(context,msg: "你點擊了刪除 $positionNow");
          },
            child: Text("刪除",style: TextStyle(fontSize: 16,color:Colors.blue),),),),
        SizedBox(height: 10,),
        Container(height: 50,width:double.infinity,margin:EdgeInsets.only(left: 15,right: 15),
          child:  FlatButton(onPressed:(){Navigator.of(context).pop();}, child: Text("取消",style: TextStyle(fontSize: 14),),),),
      ],),);
    });
  }

  void _deleteBank(){
    listKey.removeAt(positionNow);
    listBank.removeAt(positionNow);
    setState(() {});
  }

}

外部調(diào)用代碼主要需要實現(xiàn)的是
1.按下item的時候關(guān)閉已經(jīng)打開的item
2.處理刪除和修改操作

一、關(guān)閉其它打開的item

首先看看沒有實現(xiàn)這一功能時的效果是什么樣的


4.png

可以看到多個item都可以打開揉忘,那要實現(xiàn)關(guān)閉其它item這一操作需要使用globalkey跳座,首先定義個key集合,有多少條數(shù)據(jù)key也就相應(yīng)有多少個泣矛。調(diào)用item的時候把key設(shè)置給item疲眷,然后再onstart回調(diào)方法里就通過循環(huán)關(guān)閉已經(jīng)打開的item。實現(xiàn)在代碼中的注釋1 您朽、2點

二狂丝、處理刪除和修改操作

通過在回調(diào)的delete和change方法里實現(xiàn)具體操作即可,這里刪除的時候使用showModalBottomSheet彈出個底部框?qū)崿F(xiàn)詢問刪除功能哗总,點擊修改的時候顯示一個toast几颜。

到這里基本上就說完了第二種方式,flutter做這種滑動刪除操作還是比Android簡單了很多的讯屈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載蛋哭,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者。
  • 序言:七十年代末耻煤,一起剝皮案震驚了整個濱河市具壮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哈蝇,老刑警劉巖棺妓,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異炮赦,居然都是意外死亡怜跑,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來性芬,“玉大人峡眶,你說我怎么就攤上這事≈诧保” “怎么了辫樱?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵俊庇,是天一觀的道長狮暑。 經(jīng)常有香客問我,道長辉饱,這世上最難降的妖魔是什么搬男? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮彭沼,結(jié)果婚禮上缔逛,老公的妹妹穿的比我還像新娘。我一直安慰自己姓惑,他們只是感情好褐奴,可當(dāng)我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著于毙,像睡著了一般歉糜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上望众,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機與錄音伞辛,去河邊找鬼烂翰。 笑死,一個胖子當(dāng)著我的面吹牛蚤氏,可吹牛的內(nèi)容都是我干的甘耿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼竿滨,長吁一口氣:“原來是場噩夢啊……” “哼佳恬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起于游,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤毁葱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后贰剥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倾剿,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年蚌成,在試婚紗的時候發(fā)現(xiàn)自己被綠了前痘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凛捏。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖芹缔,靈堂內(nèi)的尸體忽然破棺而出坯癣,到底是詐尸還是另有隱情,我是刑警寧澤最欠,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布示罗,位于F島的核電站,受9級特大地震影響窒所,放射性物質(zhì)發(fā)生泄漏鹉勒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一吵取、第九天 我趴在偏房一處隱蔽的房頂上張望禽额。 院中可真熱鬧,春花似錦皮官、人聲如沸脯倒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藻丢。三九已至,卻和暖如春摄乒,著一層夾襖步出監(jiān)牢的瞬間悠反,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工馍佑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留斋否,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓拭荤,卻偏偏與公主長得像茵臭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子舅世,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,927評論 2 355

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