flutter:view (九)

简介: 本文介绍了Flutter中多种滚动组件的使用方法,包括`SliverAppBar`、`PageView`、`NestedScrollView`、`ListView`、`GridView`、`SingleChildScrollView`等。具体展示了如何构建可滑动的页面布局,如实现下拉刷新、无限循环的轮播图、带标题栏的嵌套滑动视图、列表视图的不同形式(如水平列表、带有分隔线的列表)以及自定义的滚动视图。还提供了监听滚动距离、滑动到指定位置等高级功能的实现代码示例。这些组件和技巧对于开发具有丰富交互效果的移动应用非常有用。

前言

在移动应用开发中,滚动组件是提升用户体验的重要元素。Flutter提供了一系列强大的滚动组件,使开发者能够轻松构建流畅且交互丰富的用户界面。本文将深入探讨Flutter中的多种滚动组件,包括SliverAppBar、PageView、NestedScrollView、ListView、GridView和SingleChildScrollView。

SliverAppBar

body: NestedScrollView(
      //   sliver  家庭组件
        headerSliverBuilder: (BuildContext context ,bool innerBoxIsScrolled){
          return [
              // buildSliverAppBar()
            SliverAppBar(title: Text('this is title'), centerTitle: true,),
          ];
        },
        body: buildBodyWidget(),
        ),

pageView


简单使用

import 'package:flutter/material.dart';
class ViewTest extends StatefulWidget {
  const ViewTest({Key? key}) : super(key: key);
  @override
  State<ViewTest> createState() => _ViewTestState();
}
class _ViewTestState extends State<ViewTest> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("view test"),
      ),
      body: PageView(
        scrollDirection: Axis.vertical, // 改变  滑动 方向
        children: [
          Center(
            child: Text(
              "this is first",
              style: Theme.of(context).textTheme.headlineLarge,
            ),
          ),
          Center(
            child: Text(
              "this is second",
              style: Theme.of(context).textTheme.headlineLarge,
            ),
          ),
          Center(
            child: Text(
              "this is third",
              style: Theme.of(context).textTheme.headlineLarge,
            ),
          ),
        ],
      ),
    );
  }
}

PageView.custom SliverChildBuilderDelegate fit: BoxFit.fill,

import 'package:flutter/material.dart';
class test extends StatefulWidget{
  const test({Key? key}) : super(key: key);
  @override
  State<StatefulWidget> createState() =>_test();
}
class _test extends State<test> {
  PageController pageController = new PageController();
  double currentPage = 0;
  List<String> imgList=[
    "imgs/happy.webp",
    "imgs/laying.jpg",
    "imgs/noodle.jpg",
    "imgs/sleep.webp",
  ];
  void initState(){
    super.initState();
    pageController = new PageController(
      initialPage: 0,
      keepPage: true,
    );
    pageController.addListener(() {
      setState(() {
        currentPage = pageController.page!;
      });
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('滑动视图'),
      ),
      body: buildPageView(),
    );
  }
  buildPageView() {
    return Container(
      height: 200,
      width: 400,
      child: PageView.custom(
          controller: pageController,
          childrenDelegate: SliverChildBuilderDelegate((BuildContext context,int index){
              if(index==currentPage.floor()){
                return Transform(
                  alignment: Alignment.center,
                    transform: Matrix4.identity()..rotateX(currentPage-index)
                    ..rotateY(0.98),
                  child: buildItem(index)
                );
              }else if(index == currentPage.floor()+1){
                return Transform(
                alignment: Alignment.center,
                    transform:Matrix4.identity()
                    ..rotateX(currentPage-index)
                    ..rotateY(0.9),
                  child: buildItem(index),
                );
              }else{
                print("currentPage is $index");
                return buildItem(index);
              }
          },
          childCount: imgList.length
          ),
      ),
    );
  }
  buildItem(int index){
    print("index $index");
    return Container(
      child: Image.asset(
        "${imgList[index]}",
        fit: BoxFit.fill,
      ),
    );
  }
}
void  main(){
  runApp(const MaterialApp(
    home:test(),
  ));
}

pageview.builder

class _ViewTestState extends State<ViewTest> {
  @override
  Widget build(BuildContext context) {
  return PageView.builder(
        scrollDirection: Axis.vertical,
    itemCount: 10,
      itemBuilder: (context,index){
        return Center(
          child: Text(
           "this is ${index+1} page",
            style: Theme.of(context).textTheme.headlineLarge,
          ),
        );
      });
   }
}

实现 无限 下拉

思路 使用  for 循环

import 'package:flutter/material.dart';
class ViewTest extends StatefulWidget {
  const ViewTest({Key? key}) : super(key: key);
  @override
  State<ViewTest> createState() => _ViewTestState();
}
class _ViewTestState extends State<ViewTest> {
  List<Widget> list = [];
  void initState() {
    super.initState();
    for (var i = 0; i < 10; i++) {
      list.add(
        Center(
          child: Text(
            "this is ${i + 1} page",
            style: TextStyle(fontSize: 40),
          ),
        ),
      );
    }
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('hello,flutter'),
      ),
      body: PageView(
          onPageChanged: (index) {
            if (index + 2 == list.length) {
              for (var i = 0; i < 10; i++) {
                setState(() {
                  list.add(
                    Center(
                      child: Text(
                        "this is ${i + 1} page",
                        style: TextStyle(fontSize: 40),
                      ),
                    ),
                  );
                });
              }
            }
          },
          scrollDirection: Axis.vertical,
          children: list),
    );
  }
}


NestedScorllVie

NestedScorllView  继承 于 Custom ScrollView

import 'package:flutter/material.dart';
class test extends StatefulWidget{
  const test({Key? key}) : super(key: key);
  @override
  State<StatefulWidget> createState() =>_test();
}
class _test extends State<test> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title:Text('test nestScorllView')),
      body: NestedScrollView(
      //   sliver  家庭组件
        headerSliverBuilder: (BuildContext context ,bool innerBoxIsScrolled){
          return [
            SliverAppBar(title: Text('this is title'), centerTitle: true,),
          ];
        },
        body: Container(
          alignment: Alignment.center,
          color: Colors.grey,
          height: 1600,
          child: Text('this is test data'),
        ),
      ),
    );
  }
}
void  main(){
  runApp(MaterialApp(
    home:test(),
  ));
}

配合listview

import 'package:flutter/material.dart';
class test extends StatefulWidget{
  const test({Key? key}) : super(key: key);
  @override
  State<StatefulWidget> createState() =>_test();
}
class _test extends State<test> with SingleTickerProviderStateMixin{
  late TabController tabController;
  @override
  void initState(){
    super.initState();
    //初始化控制器
    tabController=TabController(length: 3, vsync: this);
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title:Text('test nestScorllView')),
      body: NestedScrollView(
      //   sliver  家庭组件
        headerSliverBuilder: (BuildContext context ,bool innerBoxIsScrolled){
          return [
              // buildSliverAppBar()
            SliverAppBar(title: Text('this is title'), centerTitle: true,),
          ];
        },
        body: buildBodyWidget(),
        ),
    );
  }
  buildBodyWidget(){
    return ListView.builder(itemBuilder: (BuildContext contest ,int index){
         return Container(
         color: Colors.grey[200],
         height: 100,
          child: Container(
          margin: EdgeInsets.only(left: 8,right: 8,top: 4,bottom: 4),
          padding: EdgeInsets.all(8),
          color: Colors.white,
          child: Text('this is test data'),
        ),
      );
      },
      // 注意为这个   在listview  .builder   前面
      itemCount:200,
    );
  }
}
void  main(){
  runApp(MaterialApp(
    home:test(),
  ));
}

CustomScrollView

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title:Text('test nestScorllView')),
       body:CustomScrollView(
    controller:  scrollController,
    slivers: [
      SliverAppBar(
    title: Text('title'),
    centerTitle: true,
    )
    ],
    )
    );
  }
SliverGrid buildSliverGrid(){
  return new SliverGrid(
    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        mainAxisSpacing: 10.0,
        crossAxisSpacing: 10.0,
        childAspectRatio: 3.0,
        crossAxisCount: 2),
    delegate: SliverChildBuilderDelegate(
      (BuildContext context,int index){
        return Container(
          alignment: Alignment.center,
          color: Colors.grey[100*(index%9)],
          child: Text('grid item $index'),
        );
      },
      childCount: 20,
    ),
  );
}

ListView

简单使用

Widget buildListView(){
     return ListView(
       //  scorll direction   axis  .vertical  horizontal
       scrollDirection: Axis.vertical,
       //  默认为false   为true  时 滚动到底部
       reverse: false,
     // 回弹
       physics: BouncingScrollPhysics(),
       children: [
         Text('this is first'),
         Text('this is second'),
         Text('this is third'),
         Text('this is fourth'),
         Text('this is fifth'),
       ],
     );
  }
Widget buildListView2(){
  return ListView.builder(itemBuilder: (BuildContext contest ,int index) {
    return Container(
      color: Colors.grey[200],
      height: 100,
      child: Container(
        margin: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
        padding: EdgeInsets.all(8),
        color: Colors.cyan[100*(index % 9)],
        child: Text('this is test data'),
      ),
    );
  }
  );
  }

separator

return ListView.separated(
    itemCount: 100,
    itemBuilder: (BuildContext contest, int index) {
      return Container(
        color: Colors.grey[200],
        height: 100,
        child: Container(
          margin: EdgeInsets.only(left: 8, right: 8, top: 4, bottom: 4),
          padding: EdgeInsets.all(8),
          color: Colors.cyan[100 * (index % 9)],
          child: Text(
            'this is test data ${index}',
            style: TextStyle(decoration: TextDecoration.none),
          ),
        ),
      );
    },
    separatorBuilder: (BuildContext context, int index) {
      return Divider(
        color: Colors.red,
        indent: 30,
        endIndent: 30,
        thickness: 10,
      );
    },
  );

懒加载的方式

Widget buildListView3() {
return ListView.custom(
    childrenDelegate:
    SliverChildBuilderDelegate((BuildContext context,int index){
     return new Container(
        height: 40,
color: Colors.cyan[100* (index % 9)],
);
},
      childCount:20,
    )
);
  }


使用图片加文字

简单使用文字和图片

return ListView(
      padding: EdgeInsets.fromLTRB(0,10,0,0),
      children: <Widget>[
// title
        ListTile(
          leading: Icon(Icons.home),
          title: Text('i am a home'),
          trailing: Icon(Icons.home),
        ),
//   加一个下划线
        Divider(),
        ListTile(
          leading: Icon(Icons.assignment),
          title: Text('i am a assignment'),
          subtitle:Text('i am a assignment2') ,
        ),
        Divider(),
        ListTile(
          leading: Icon(Icons.payment,color: Colors.red,),
          title: Text('i am a payment'),
        ),
        Divider(),
        ListTile(
          leading: Icon(Icons.add_box),
          title: Text('i am a add_box'),
        ),
        Divider(),
      ],
    );

一张图片加一段文字

return ListView(
      children: [
        Image.asset('./assets/imgs/laying.jpg'),
        Container(
          padding: EdgeInsets.fromLTRB(0,10,0,0),
          child: Text('i am a photo',
          textAlign: TextAlign.center,
          style: TextStyle(fontSize: 22),),
        ),
        Image.asset('./assets/imgs/laying.jpg'),
        Container(
          padding: EdgeInsets.fromLTRB(0,10,0,0),
          child: Text('i am a photo',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 22),),
        )
      ],
    );

水平列表

scorllDirect:Axis.horizontal

Widget build(BuildContext context) {
    return SizedBox(
        height: 100,
        child: ListView(
          scrollDirection: Axis.horizontal, // 水平列表  高度自适应
          padding: const EdgeInsets.all(10),
          children: <Widget>[
            Container(
              // height:120,
              width: 100, // 宽度 设置没用,  自适应
              child: Column(
                children: [
                 SizedBox(
                   height:80,
                   width: 80,
                   child:  Image.asset("./assets/imgs/laying.jpg"),
                 ),
                  Text('this is a sentence')
                ],
              ),
            ),
            Container(
              width: 100, // 宽度 设置没用,  自适应
              decoration: const BoxDecoration(color: Colors.blue),
            ),
          ],
        )
    );
  容器里面套容器,
  将  默认的 竖  改为横  horizontal

GridView

Widget buildGridView1(){
    return GridView(gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      // cross  axis  每行的个数
      crossAxisCount: 2,
      // 空间
      mainAxisSpacing: 10,
      crossAxisSpacing: 10,
        // 长度比例
        childAspectRatio: 1.4
    ),
    children: [
        buildListViewItem(1),
        buildListViewItem(2),
        buildListViewItem(3),
        buildListViewItem(4),
        buildListViewItem(5),
        buildListViewItem(6),
    ],
    );
  }
  Widget buildListViewItem(int index){
    return new Container(
      height: 84,
      alignment: Alignment.center,
      color: Colors.grey[100*(index % 9)],
      child: new Text('grid item $index'),
    );
  }
Widget buildGridView() {
  return GridView(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
          // cross  axis  每行的个数
          crossAxisCount: 3,
          // 空间
          mainAxisSpacing: 10,
          crossAxisSpacing: 10,
          // 长度比例
          childAspectRatio: 1.4),
      children: List.generate(50, (index) {
        return Container(
          color: Color.fromARGB(255, Random().nextInt(256),
              Random().nextInt(256), Random().nextInt(256)),
        );
      }));
}
Widget buildGridView2() {
  return GridView(
    gridDelegate:
        //  给长度  最长  的 为  220   大概 值
        SliverGridDelegateWithMaxCrossAxisExtent(
            crossAxisSpacing: 8,
            mainAxisSpacing: 8,
            childAspectRatio: 1.6,
            maxCrossAxisExtent: 220),
    children: List.generate(50, (index) {
      return Container(
        color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),
            Random().nextInt(256)),
      );
    }),
  );
}
Widget buildGridView2() {
  return GridView.builder(
    gridDelegate:
        //  给长度  最长  的 为  220   大概 值
        SliverGridDelegateWithMaxCrossAxisExtent(
            crossAxisSpacing: 8,
            mainAxisSpacing: 8,
            childAspectRatio: 1.6,
            maxCrossAxisExtent: 220),
    itemBuilder: (BuildContext context, int index) {
      return Container(
        color: Color.fromARGB(255, Random().nextInt(256), Random().nextInt(256),
            Random().nextInt(256)),
      );
    },
  );
}


下拉刷新更多


return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text('this is title'),
      ),
      body: RefreshIndicator(
        color: Colors.blue,
        // 下拉停止距离
        displacement: 44.0,
        backgroundColor: Colors.grey[200],
        onRefresh: ()async{
          await Future.delayed(Duration(milliseconds: 3000));
          return Future.value(true);
        },
        child: buildGridView(),
      ),
    );

page  swiper

先 定义 一个   图片 组件

所给 的  值 为 自己 定义 的值 可以 传参

import 'package:flutter/material.dart';
//  单个组件 后面 方便 使用
class ImagePage extends StatelessWidget {
  final double width;
  final double height;
  final String src;
  const ImagePage({ super.key,this.width=double.infinity,this.height=200,required this.src});
  @override
  Widget build(BuildContext context) {
  //    定义 组件     设置 属性 在 组件  里面
  return SizedBox(
    width: width,
    height: height,
    child: Image.asset(src,fit:BoxFit.cover),
  // child: Text("hello,world"),
  );
  }
}

定义一个 list  存放  图片

List<Widget> list = [];
  @override
  void initState() {
    super.initState();
    list = [
      ImagePage(
        width: double.infinity,
        height: 200,
        src: "imgs/1.png",
      ),
      ImagePage(
        width: double.infinity,
        height: 200,
        src: "imgs/laying.jpg",
      ),
      ImagePage(
        width: double.infinity,
        height: 200,
        src: "imgs/noodle.jpg",
      ),
    ];
  }

使用

body: SizedBox(
            height: 400,
            width: 400,
            child:PageView(
              children: list,
            )
        ),

可以  无限  循环

body: SizedBox(
            height: 400,
            width: 400,
            child:PageView.builder(
              itemCount: 1000,
              itemBuilder: (context,index){
                //0 % 3  = 0   1 %3 = 1   2 % 3 = 2   3  % 3 =  1
                return list[index % list.length];
              },
            )
        ),

用 stack  在  图片 下面 加 3  个 小点

Positioned(
              //  right  加  left   都 为 0  就会  占满 整行
              //  之后 便可 居中
              right: 0,
                left: 0,
                bottom: 2,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: List.generate(3, (index) {
                    return Container(
                      margin: EdgeInsets.all(5),
                      width: 10,
                      height: 10,
                      decoration: BoxDecoration(
                        color: _currentIndex==index?Colors.blue:Colors.grey,
                        // borderRadius: BorderRadius.circular(5)
                      shape: BoxShape.circle
                      ),
                    );
                  }).toList(),
                ),
            )
定义一个  变量   存储  此时的 位置
  int _currentIndex=0;
    color: _currentIndex==index?Colors.blue:Colors.grey,
      当前 的  为 蓝色

全部 代码


SingleChildScrollView

body: Center(
        child: SingleChildScrollView(
          padding: EdgeInsets.all(20),
          // 滑倒边界有回弹效果
          physics: BouncingScrollPhysics(),
          controller: _scrollController,
          child: Container(
              color: Colors.grey,
              height: 1000,
              child: Align(
                alignment: FractionalOffset.bottomCenter,
                child: Text(
                  'Hello, Flutter!',
                  style: TextStyle(
                    fontSize: 20,
                    color: Colors.white,
                  ),
                ),
              )),
        ),
      ),

监听距离

void initState() {
    // TODO: implement initState
    super.initState();
    _scrollController.addListener(() {
      double offsetValue=_scrollController.offset;
      double max= _scrollController.position.maxScrollExtent;
      if(offsetValue<=0){
        print("已经在顶部啦");
      }else if(offsetValue>=max){
        print("已经到底部啦");
      }else{
        print("the offsetValue is: $offsetValue max: $max");
      }
    });
  }

滑动到指定位置  高度

void scrollOffset(double offset) {
    _scrollController.animateTo(offset,
        duration: Duration(milliseconds: 200), curve: Curves.ease);
  }
void scrollToTop() {
    scrollOffset(0.0);
  }
  void scrollToBottom() {
    double maxScroll = _scrollController.position.maxScrollExtent;
    scrollOffset(maxScroll);
  }

使用

children: [
                ElevatedButton(
                  onPressed: () {
                    scrollToBottom();
                  },
                  child: Text("scrollToBottom"),
                ),
              SizedBox(height:900),
                ElevatedButton(
                  onPressed: () {
                    scrollToTop();
                  },
                  child: Text("scrollToTop"),
                ),
              ],


相关文章
|
前端开发 vr&ar 容器
Flutter 115: 图解自定义 View 之 Canvas (四) drawParagraph
0 基础学习 Flutter,第一百一十五节:自定义 Canvas 第四节,文本绘制小结!
667 0
Flutter 115: 图解自定义 View 之 Canvas (四) drawParagraph
|
前端开发 Android开发 iOS开发
Flutter 35: 图解自定义 View 之 Canvas (二)
0 基础学习 Flutter,第三十五步:自定义 View 第三节~
4710 0
|
前端开发 Android开发 存储
Flutter 36: 图解自定义 View 之 Canvas (三)
0 基础学习 Flutter,第三十六步:自定义 View 第四节~
2030 0
|
前端开发
Flutter 34: 图解自定义 View 之 Canvas (一)
0 基础学习 Flutter,第三十四步:自定义 View 第二节~
2676 0
|
前端开发 Android开发
Flutter 33: 图解自定义 View 之 Paint
0 基础学习 Flutter,第三十三步:自定义 View 第一节~
2529 0
|
2月前
|
Android开发 iOS开发 容器
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
|
8天前
|
传感器 前端开发 Android开发
在 Flutter 开发中,插件开发与集成至关重要,它能扩展应用功能,满足复杂业务需求
在 Flutter 开发中,插件开发与集成至关重要,它能扩展应用功能,满足复杂业务需求。本文深入探讨了插件开发的基本概念、流程、集成方法、常见类型及开发实例,如相机插件的开发步骤,同时强调了版本兼容性、性能优化等注意事项,并展望了插件开发的未来趋势。
22 2
|
2月前
|
开发者
鸿蒙Flutter实战:07-混合开发
鸿蒙Flutter混合开发支持两种模式:1) 基于har包,便于主项目开发者无需关心Flutter细节,但不支持热重载;2) 基于源码依赖,利于代码维护与热重载,需配置Flutter环境。项目结构包括AppScope、flutter_module等目录,适用于不同开发需求。
80 3
|
26天前
|
传感器 开发框架 物联网
鸿蒙next选择 Flutter 开发跨平台应用的原因
鸿蒙(HarmonyOS)是华为推出的一款旨在实现多设备无缝连接的操作系统。为了实现这一目标,鸿蒙选择了 Flutter 作为主要的跨平台应用开发框架。Flutter 的跨平台能力、高性能、丰富的生态支持和与鸿蒙系统的良好兼容性,使其成为理想的选择。通过 Flutter,开发者可以高效地构建和部署多平台应用,推动鸿蒙生态的快速发展。
170 0
|
28天前
|
Dart 安全 UED
Flutter&鸿蒙next中的表单封装:提升开发效率与用户体验
在移动应用开发中,表单是用户与应用交互的重要界面。本文介绍了如何在Flutter中封装表单,以提升开发效率和用户体验。通过代码复用、集中管理和一致性的优势,封装表单组件可以简化开发流程。文章详细讲解了Flutter表单的基础、封装方法和表单验证技巧,帮助开发者构建健壮且用户友好的应用。
65 0