美文网首页
学习Flutter的第七天(组件5)

学习Flutter的第七天(组件5)

作者: 囧rg | 来源:发表于2023-04-24 22:30 被阅读0次

组件1:MaterialApp、Container、Text、Image、Icon
组件2:ListView、GridView
组件3:Padding、Row 、Column、Stack、Align、Positioned
组件4:AspectRatio、Row 、Button
组件5:Wrap、StatelessWidget 、StatefulWidget、Dialog、PageView、TextField

3.16 Wrap

Wrap可以为子控件进行水平或者垂直方向布局,且当空间用完时,Wrap会自动换行,也是常说的流式布局

名称 功能
direction 排列方向,默认水平方向排列
alignment 子控件在主轴上的对齐方式
spacing 主轴上子控件中间的间距
runAlignment 子控件在交叉轴上的对齐方式
runSpacing 交叉轴上子控件之间的间距
crossAxisAlignment 交叉轴上子控件的对齐方式
textDirection textDirection水平方向上子控件的起始位置
verticalDirection 垂直方向上子控件的其实位置
children

3.17 StatelessWidget 和 StatefulWidget

参考:https://blog.csdn.net/qq_30963589/article/details/112561639

在Flutter开发中,我们一般都不用直接继承Widget类来实现一个新组件,相反,我们通常会通过继承StatelessWidget或StatefulWidget来间接继承Widget类来实现。
StatelessWidget和StatefulWidget都是直接继承自Widget类,而这两个类也正是Flutter中非常重要的两个抽象类,它们引入了两种Widget模型.

3.17.1 StatelessWidget

StatelessWidget继承于Widget类,他重写了creatElement()方法。

@override
StatelessElement createElement() => new StatelessElement(this);

StatelessWidget用于不需要维护状态的场景,它通常在build方法中通过嵌套其它Widget来构建UI,在构建过程中会递归的构建其嵌套的Widget

我们一般构建静态的UI的时候会使用StatelessWidget

3.17.2 StatefulWidget

StatefulWidget也是继承于Widget类,内部也重写了creatElement(),不过与StatelessWidget不同的是返回的Element 对象并不相同;
另外StatefulWidget类中添加了一个新的接口createState()

abstract class StatefulWidget extends Widget {
  // 为子类初始化[key]。
  const StatefulWidget({ Key key }) : super(key: key);

  //创建一个[StatefulElement]来管理此小部件在树中的位置。
  @override
  StatefulElement createElement() => StatefulElement(this);

  // 在树中的给定位置为此小部件创建可变状态
  @protected
  @factory
  State createState();
}

StatefulWidget来说,State是核心,State表示与其对应的StatefulWidget要维护的状态.一个StatefulWidget对应一个State.

State保存的信息:

在widget 构建时可以被同步读取。
在widget生命周期中可以被改变,当State被改变时,可以手动调用其setState()方法通知Flutter framework状态发生改变,Flutter framework在收到消息后,会重新调用其build方法重新构建widget树,从而达到更新UI的目的。

每次 setState() 的时候都会重新调用 build方法,所以有些组件前面加 const 关键字,是为了再次build的时候,不会重新创建该组件。虽然不加const也不会有错误,但是对性能会有影响。

动态添加列表代码

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: HomePage(),
  ));
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final List _dataList = [];
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Demo"),
      ),
      body: ListView(
        children: _dataList.map((e) {
          return ListTile(
            title: e,
          );
        }).toList(),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _count++;
            _dataList.add(Text("列表项 $_count"));
          });
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

3.18 Dialog

3.18.1 AlertDialog

AlertDialog 是一个用于向用户传递信息的弹出层

import 'package:flutter/material.dart';

class Category extends StatefulWidget {
  const Category({super.key});

  @override
  State<Category> createState() => _CategoryState();
}

class _CategoryState extends State<Category> {
  void _alertDialog() async {
    var result = await showDialog(
        context: context,
        builder: ((context) {
          return AlertDialog(
            title: const Text("提示信息!"),
            content: const Text("这是提示的信息内容"),
            actions: [
              TextButton(
                  onPressed: () {
                    Navigator.of(context).pop("ok"); // 点击按钮让AlertDialog消失
                  },
                  child: const Text("确定")),
              TextButton(
                  onPressed: () {
                    Navigator.of(context).pop("cancel");
                  },
                  child: const Text("取消"))
            ],
          );
        }));
    print(result);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        ElevatedButton( onPressed: _alertDialog, child: const Text("AlertDialog"))
      ],
    );
  }
}

3.18.2 SimpleDialog

为用户几个提供选项,可选标题,标题在选项上不边的信息弹出窗.

import 'package:flutter/material.dart';

class Category extends StatefulWidget {
  const Category({super.key});

  @override
  State<Category> createState() => _CategoryState();
}

class _CategoryState extends State<Category> {

  void _simpleDialog() async {
    var result = await showDialog(
        // 点击背景灰色dialog消失
        barrierDismissible: true,
        context: context,
        builder: ((context) {
          return SimpleDialog(
            title: const Text("请选择语言"),
            children: [
              SimpleDialogOption(
                onPressed: () {
                  Navigator.pop(context, "chinese");
                },
                child: const Text("汉语"),
              ),
              const Divider(),
              SimpleDialogOption(
                onPressed: () {
                  Navigator.pop(context, "english");
                },
                child: const Text("英语"),
              ),
              const Divider(),
              SimpleDialogOption(
                onPressed: () {
                  Navigator.pop(context, "Janese");
                },
                child: const Text("日语"),
              ),
            ],
          );
        }));
    print(result);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        ElevatedButton( onPressed: _simpleDialog, child: const Text("SimpleDialog"))
      ],
    );
  }
}

3.18.3 showModalBottomSheet

底部面板,相当于弹出了一个新页面默认点击消失,可以给子组件外面包一层GestureDetector并设置onTap返回false,拦截点击事件使点击底部面板区域,面板不消失。底部面板的高度是有限制的,不能设置全屏高度

import 'package:flutter/material.dart';

class Category extends StatefulWidget {
  const Category({super.key});

  @override
  State<Category> createState() => _CategoryState();
}

class _CategoryState extends State<Category> {
  
  void _modelBottomSheet() async {
    var result = await showModalBottomSheet(
        context: context,
        builder: ((context) {
          return SizedBox(
            height: 200,
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                ListTile(
                  title: const Text("分享"),
                  onTap: () {
                    Navigator.of(context).pop("share");
                  },
                ),
                Divider(),
                ListTile(
                  title: const Text("收藏"),
                  onTap: () {
                    Navigator.of(context).pop("shoucang");
                  },
                ),
                Divider(),
                ListTile(
                  title: const Text("取消"),
                  onTap: () {
                    Navigator.of(context).pop("cancel");
                  },
                )
              ],
            ),
          );
        }));
    print(result);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        ElevatedButton( onPressed: _modelBottomSheet, child: const Text("BottomSheet")),
      ],
    );
  }
}

3.19 PageView

名称 功能
scrollDirection Axis.horizontal水平方向,Axis.vertical垂直方向
children
allowImplicitScrolling 缓存前2页

PageView控件可以实现一个“图片轮播”的效果,PageView不仅可以水平滑动也可以垂直滑动,简单用法如下:

PageView(
    children: <Widget>[
        MyPage1(),    
        MyPage2(), 
        MyPage3(),    
    ],
)

使用 PageView.builder

import 'package:flutter/material.dart';

class BannerBuilder extends StatefulWidget {
  const BannerBuilder({super.key});

  @override
  State<BannerBuilder> createState() => _BannerBuilderState();
}

class _BannerBuilderState extends State<BannerBuilder> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("pageViewBuileer"),
      ),
      body: PageView.builder(
          //当页面选中后回调此方法
          //参数[index]是当前滑动到的页面角标索引 从0开始
          onPageChanged: (int index) {
            print("当前的页面是 $index");
          },
          //值为flase时 显示第一个页面 然后从左向右开始滑动
          //值为true时 显示最后一个页面 然后从右向左开始滑动
          reverse: false,
          //滑动到页面底部无回弹效果
          physics: BouncingScrollPhysics(),
          itemCount: 10,
          //纵向滑动切换
          scrollDirection: Axis.vertical,
          itemBuilder: (context, index) {
            return Center(
                child: Text(
              "第${index + 1}屏",
              style: Theme.of(context).textTheme.bodyText1,
            ));
          }),
    );
  }
}

使用pageController 和 timer 控制自动轮播

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

class BannerBuilder extends StatefulWidget {
  const BannerBuilder({super.key});

  @override
  State<BannerBuilder> createState() => _BannerBuilderState();
}

class _BannerBuilderState extends State<BannerBuilder> {
  late PageController _pageController;
  late Timer timer;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
        // 初始化并设置
    _pageController = PageController(initialPage: 0);
    timer = Timer.periodic(const Duration(seconds: 5), (timer) {
      // 参数1:跳到索引,参数2:动画时间,参数3:动画模式
      _pageController.animateToPage(2,
          duration: const Duration(milliseconds: 200), curve: Curves.linear);
    });
  }

  @override
  void dispose() {
    // 销毁组件
    super.dispose();
    timer.cancel();
    _pageController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("pageViewBuileer"),
      ),
      body: PageView.builder(
          // 设置controller
          controller: _pageController,
          // ...
    );
  }
}

3.20 TextField

const TextField({
    Key key,
    this.controller,//控制器
    this.focusNode,//焦点
    this.obscureText = false,//是否隐藏文本,即显示密码类型
    this.maxLines = 1,//最多行数,高度与行数同步
    this.autofocus = false,//自动聚焦
    this.decoration = const InputDecoration(),//装饰
    TextInputType keyboardType,//键盘类型,即输入类型
    this.onChanged,//输入改变回调

    //以下属性不常用,用到再来查看
    this.textInputAction,//键盘按钮
    this.textCapitalization = TextCapitalization.none,//大小写
    this.style,//样式
    this.strutStyle,
    this.textAlign = TextAlign.start,//对齐方式
    this.textDirection,
    this.autocorrect = true,//自动更正
    this.minLines,//最小行数
    this.expands = false,
    this.maxLength,//最多输入数,有值后右下角就会有一个计数器
    this.maxLengthEnforced = true,
    this.onEditingComplete,//输入完成时,配合TextInputAction.done使用
    this.onSubmitted,//提交时,配合TextInputAction
    this.inputFormatters,//输入校验
    this.enabled,//是否可用
    this.cursorWidth = 2.0,//光标宽度
    this.cursorRadius,//光标圆角
    this.cursorColor,//光标颜色
    this.keyboardAppearance,
    this.scrollPadding = const EdgeInsets.all(20.0),
    this.dragStartBehavior = DragStartBehavior.start,
    this.enableInteractiveSelection,
    this.onTap,//点击事件
    this.buildCounter,
    this.scrollPhysics,
  }) 

其中decoration接收一个InputDecoration类型的值,主要用于控制TextField的外观以及提示信息等 这里介绍下这个重要的Widget,

InputDecoration({
    this.icon,    //位于装饰器外部和输入框前面的图片
    this.labelText,  //用于描述输入框,例如这个输入框是用来输入用户名还是密码的,当输入框获取焦点时默认会浮动到上方,
    this.labelStyle,  // 控制labelText的样式,接收一个TextStyle类型的值
    this.helperText, //辅助文本,位于输入框下方,如果errorText不为空的话,则helperText不会显示
    this.helperStyle, //helperText的样式
    this.hintText,  //提示文本,位于输入框内部
    this.hintStyle, //hintText的样式
    this.hintMaxLines, //提示信息最大行数
    this.errorText,  //错误信息提示
    this.errorStyle, //errorText的样式
    this.errorMaxLines,   //errorText最大行数
    this.hasFloatingPlaceholder = true,  //labelText是否浮动,默认为true,修改为false则labelText在输入框获取焦点时不会浮动且不显示
    this.isDense,   //改变输入框是否为密集型,默认为false,修改为true时,图标及间距会变小
    this.contentPadding, //内间距
    this.prefixIcon,  //位于输入框内部起始位置的图标。
    this.prefix,   //预先填充的Widget,跟prefixText同时只能出现一个
    this.prefixText,  //预填充的文本,例如手机号前面预先加上区号等
    this.prefixStyle,  //prefixText的样式
    this.suffixIcon, //位于输入框后面的图片,例如一般输入框后面会有个眼睛,控制输入内容是否明文
    this.suffix,  //位于输入框尾部的控件,同样的不能和suffixText同时使用
    this.suffixText,//位于尾部的填充文字
    this.suffixStyle,  //suffixText的样式
    this.counter,//位于输入框右下方的小控件,不能和counterText同时使用
    this.counterText,//位于右下方显示的文本,常用于显示输入的字符数量
    this.counterStyle, //counterText的样式
    this.filled,  //如果为true,则输入使用fillColor指定的颜色填充
    this.fillColor,  //相当于输入框的背景颜色
    this.errorBorder,   //errorText不为空,输入框没有焦点时要显示的边框
    this.focusedBorder,  //输入框有焦点时的边框,如果errorText不为空的话,该属性无效
    this.focusedErrorBorder,  //errorText不为空时,输入框有焦点时的边框
    this.disabledBorder,  //输入框禁用时显示的边框,如果errorText不为空的话,该属性无效
    this.enabledBorder,  //输入框可用时显示的边框,如果errorText不为空的话,该属性无效
    this.border, //正常情况下的border
    this.enabled = true,  //输入框是否可用
    this.semanticCounterText,  
    this.alignLabelWithHint,
  })

修改下划线颜色,要同时修改2个样式

enabledBorder: UnderlineInputBorder(
  borderSide: BorderSide(color: Color(0xffeeeeee)),
),
focusedBorder: UnderlineInputBorder(
  borderSide: BorderSide(color: Colors.blue),
),

无边框

TextField(
  decoration: InputDecoration(border: InputBorder.none),
)

相关文章

网友评论

      本文标题:学习Flutter的第七天(组件5)

      本文链接:https://www.haomeiwen.com/subject/eirpjdtx.html