Flutter入門05 -- 滾動(dòng)組件

  • 滾動(dòng)組件有ListView,GridViewSliver

ListView

  • ListView創(chuàng)建的方式通常有三種,分別為ListView(),ListView.builder(),ListView.separated()
ListView創(chuàng)建方式
  • 第一種方式:ListView()
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("基礎(chǔ)widget")), body: SFHomeContent());
  }
}

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView(
      scrollDirection: Axis.vertical,
      itemExtent: 100,//設(shè)置Item的高度
      children: List.generate(100, (index) {
        return ListTile(
          leading: Icon(Icons.people),
          trailing: Icon(Icons.delete),
          title: Text("聯(lián)系人${index+1}"),
          subtitle: Text("聯(lián)系人電話號(hào)碼:19991604555"),
        );
      }),
    );
  }
}
  • ListTile組件就是ListView的Item;
  • itemExtent:設(shè)置Item的高度;
  • 效果圖如下:
image.png
  • 通過ListView()創(chuàng)建,會(huì)一次性創(chuàng)建100個(gè)Item,這樣性能比較差,其適用于Item個(gè)數(shù)確定,且數(shù)量較少的情況下才會(huì)采用;

  • 第二種方式:ListView.builder()

  • ListView.builder()不會(huì)一次性創(chuàng)建所有Item,而是需要展示的Item才會(huì)去創(chuàng)建,性能較好;

  • 案例代碼如下:

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
        itemCount: 100,
        itemExtent: 50,
        itemBuilder: (BuildContext ctx, int index){
          return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
    }
    );
  }
}
  • 第三種方式: ListView.separated()
  • 案例代碼如下:
class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ListView.separated(
        itemBuilder: (BuildContext ctx, int index){
          return Text("Hello World!!! ${index}",style: TextStyle(fontSize: 20),);
        },
        //分割線
        separatorBuilder: (BuildContext ctx,int index){
          return Divider(color: Colors.red,indent: 20,endIndent: 20,thickness: 5);
        },
        itemCount: 100
    );
  }
}
  • itemBuilder:創(chuàng)建Item;
  • separatorBuilder:創(chuàng)建分割線;

GridView

  • GridView的創(chuàng)建方式有:GridView(),GridView.builder()
  • 案例代碼一:GridView()+ SliverGridDelegateWithFixedCrossAxisCount
class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GridView(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 3,
        childAspectRatio: 1.5,
        crossAxisSpacing: 8,
        mainAxisSpacing: 8
      ),
      children:
        List.generate(100, (index) {
           return Container(
             color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256), Random().nextInt(256)),
           );
        }),
    );
  }
}
  • SliverGridDelegateWithFixedCrossAxisCount:交叉軸方向上item數(shù)量固定,其寬度根據(jù)屏幕的寬度與item的數(shù)量進(jìn)行計(jì)算;

    • crossAxisCount:item的個(gè)數(shù);
    • childAspectRatio:item的寬高比;
    • crossAxisSpacing:交叉軸方向上 item之間的間距;
    • mainAxisSpacing:主軸方向上 item之間的間距;
  • 案例代碼二:GridView()+ SliverGridDelegateWithMaxCrossAxisExtent

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GridView(
      gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 220,
        crossAxisSpacing: 8,
        mainAxisSpacing: 8,
        childAspectRatio: 1.5
      ),
      children: List.generate(100, (index) {
        return Container(
          color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
        );
      })
    );
  }
}
  • SliverGridDelegateWithMaxCrossAxisExtent:交叉軸方向上的設(shè)置item寬度,個(gè)數(shù)不固定;

    • maxCrossAxisExtent:item的最大寬度;
  • 案例代碼三:GridView.builder() + SliverGridDelegateWithFixedCrossAxisCount

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          crossAxisCount: 3,
          crossAxisSpacing: 8,
          mainAxisSpacing: 8
        ),
        itemBuilder: (BuildContext ctx,int index){
          return Container(
            color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
          );
        }
    );
  }
}

Slivers -- CustomScrollView

  • CustomScrollView:自定義滾動(dòng)組件,需要傳入Slivers即Sliver數(shù)組,我們知道ListViewGridView都是繼承自BoxScrollView,而BoxScrollView是一個(gè)抽象類,從源碼來看ListViewGridView在察創(chuàng)建的過程中都需要執(zhí)行buildSlivers方法,其內(nèi)部調(diào)用buildChildLayout方法,這是一個(gè)抽象方法,分別由ListViewGridView來實(shí)現(xiàn),最終提供一個(gè)Sliver數(shù)組,其中ListView提供的Sliver為SliverFixedExtentList或者是SliverList,GridView提供的Sliver為SliverGrid

  • 案例代碼:?jiǎn)蝹€(gè)Sliver

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

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text("基礎(chǔ)widget")),
        body: SFHomeContent());
  }
}

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverDemo1();
  }
}

class SliverDemo1 extends StatelessWidget {
  const SliverDemo1({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverSafeArea(
          sliver: SliverPadding(
            padding: EdgeInsets.all(8),
            sliver: SliverGrid(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
                childAspectRatio: 1.5
              ),
              delegate: SliverChildBuilderDelegate(
                  (BuildContext ctx,int index){
                    return Container(
                      color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
                    );
                  },
                childCount: 100
              ),
            ),
          ),
        )
      ],
    );
  }
}
  • 自定義CustomScrollView,需傳入Slivers數(shù)組,這里傳入的Sliver為SliverGrid;

  • 參數(shù)gridDelegate:是提供布局信息;

  • 參數(shù)delegate:是提供item組件,類型為SliverChildDelegate;

  • SliverChildDelegate是抽象類,其作用是用來創(chuàng)建滾動(dòng)組件的item,其有兩個(gè)子類分別為SliverChildListDelegateSliverChildBuilderDelegate

    • SliverChildListDelegate:性能較差,item一次性創(chuàng)建所有;
    • SliverChildBuilderDelegate:性能較好,創(chuàng)建需要展示的item;
  • SafeAreaSliverSafeArea的區(qū)別:

    • SafeArea:安全區(qū)域,讓目標(biāo)組件在安全區(qū)域內(nèi)顯示;
    • SliverSafeArea:給Sliver設(shè)置安全區(qū)域,且在滾動(dòng)時(shí)可以在非安全區(qū)域內(nèi)滾動(dòng),而SafeArea不可以;
  • SliverPadding:是Sliver自己的設(shè)置內(nèi)邊距的組件;

  • 案例代碼:多個(gè)Sliver

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

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        // appBar: AppBar(title: Text("基礎(chǔ)widget")),
        body: SFHomeContent());
  }
}

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        SliverAppBar(
          pinned: true,
          expandedHeight: 200,
          flexibleSpace: FlexibleSpaceBar(
            title: Text("Hello World!!",style: TextStyle(fontSize: 25),),
          ),
        ),
        SliverGrid(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              crossAxisSpacing: 8,
              mainAxisSpacing: 8,
              childAspectRatio: 2.5
          ),
            delegate: SliverChildBuilderDelegate(
                    (BuildContext ctx,int index){
                  return Container(
                    color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),Random().nextInt(256)),
                  );
                },
                childCount: 10
            ),
        ),
        SliverList(
          delegate: SliverChildBuilderDelegate(
              (BuildContext ctx,int index){
                return ListTile(
                  leading: Icon(Icons.people),
                  title: Text("聯(lián)系人$index"),
                );
              },
            childCount: 20
          ),
        )
      ],
    );
  }
}
  • slivers數(shù)組中傳入了SliverAppBar,SliverGridSliverList三種類型的Sliver,效果如下:
    image.png

滾動(dòng)組件的監(jiān)聽

  • 滾動(dòng)組件的監(jiān)聽通常有兩種方式,分別為controllerNotificationListener
controller監(jiān)聽
  • 可以設(shè)置默認(rèn)值offset;
  • 監(jiān)聽滾動(dòng),也可以監(jiān)聽滾動(dòng)的位置;
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatefulWidget {
  @override
  _SFHomePageState createState() => _SFHomePageState();
}

class _SFHomePageState extends State<SFHomePage> {
  ScrollController controller = ScrollController(initialScrollOffset: 300);
  bool isShowFloatButton = false;

  @override
  void initState() {
    super.initState();
    controller.addListener(() {
      print("監(jiān)聽到滾動(dòng): ${controller.offset}");
      setState(() {
        isShowFloatButton = controller.offset >= 1000;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("基礎(chǔ)widget")),
      body: SFHomeContent(controller),
      floatingActionButton: isShowFloatButton ? FloatingActionButton(
        child: Icon(Icons.arrow_upward),
        onPressed: (){
          controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.easeIn);
        },
      ) : null,
    );
  }
}

class SFHomeContent extends StatelessWidget {
  final ScrollController controller;
  SFHomeContent(this.controller);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      controller: controller,
      itemBuilder: (BuildContext ctx, int index) {
        return ListTile(
          leading: Icon(Icons.people),
          title: Text("聯(lián)系人$index"),
        );
      },
      itemCount: 100,
    );
  }
}
  • 右下角懸浮按鈕,當(dāng)前滾動(dòng)偏移量>=1000時(shí)顯示;
NotificationListener監(jiān)聽
  • 案例代碼:
import 'package:flutter/material.dart';

void main() => runApp(SFMyApp());

class SFMyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(home: SFHomePage());
  }
}

class SFHomePage extends StatefulWidget {
  @override
  _SFHomePageState createState() => _SFHomePageState();
}

class _SFHomePageState extends State<SFHomePage> {
  bool isShowFloatButton = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("基礎(chǔ)widget")),
      body: SFHomeContent(),
      floatingActionButton: isShowFloatButton
          ? FloatingActionButton(
              child: Icon(Icons.arrow_upward),
              onPressed: () {

              },
            )
          : null,
    );
  }
}

class SFHomeContent extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return NotificationListener(
      onNotification: (ScrollNotification notification){
        if(notification is ScrollStartNotification){
          print("開始滾動(dòng)");
        }else if (notification is ScrollUpdateNotification){
          print("正在滾動(dòng) -- 總區(qū)域:${notification.metrics.maxScrollExtent} 當(dāng)前位置: ${notification.metrics.pixels}");
        }else if (notification is ScrollEndNotification){
          print("結(jié)束滾動(dòng)");

        }

        return true;
      },
      child: ListView.builder(
        itemBuilder: (BuildContext ctx, int index) {
          return ListTile(
            leading: Icon(Icons.people),
            title: Text("聯(lián)系人$index"),
          );
        },
        itemCount: 100,
      ),
    );
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容