Flutter基础(纯新手笔记)

226 阅读5分钟
  1. 有状态和无状态的控件
// StatefulWidget 有状态(更新)的控件
class MyText extends StatefulWidget {}
// 由两个类组成如:Test类和_Test类

// StatelessWidget 无状态(更新)的控件
class MyText extends StatelessWidget {}

// 有状态控件写法如下

class Test extends StatefulWidget {
  const Test({super.key, required this.params});

  final Map? params;

  @override // 复写,拿父类有的方法来重写类似render
  State<Test> createState() => _Test(); // 创建一个_Test类
}

class _Test extends State<Test> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return 控件;
  }
}

2.颜色码的使用注意事项:

// 不能使用三位缩写颜色码,如#333必须改为#333333;

3.交互控件

GestureDetector

// 常用回调
// onTap 点击
// onTapDown 按下
// onTapUp 抬起
// onTapCancel 点击取消事件,比如点击某个按钮后未抬起手指状态下移出按钮
// onDoubleTap 双击
// onDoubleTapDown 双击按下
// onDoubleTapUp 双击抬起
// onDoubleTapCancel 双击取消
// onLongPress 长按

4.超出屏幕解决方法

SingleChildScrollView

// 注意事项:只适合稍微超出的内容,长列表不适合,长列表使用listview控件
// 其他注意事项:横向溢出一般用Expanded

5.Row控件

行内子控件宽度自适应:mainAxisSize: MainAxisSize.min
排列方式:mainAxisAlignment: MainAxisAlignment.spaceBetween;crossAxisAlignment: CrossAxisAlignment.start

6.外边框、圆角写法

decoration: BoxDecoration(
    borderRadius:
        const BorderRadius.all(Radius.circular(12)),
        color: HexColor('#FFFFFF')
) // 12圆角白色底的矩形框
  shape: BoxShape.circle, // 圆
 // 圆角图片(背景)
  Container(
    width: 100,
    height: 100,
    decoration: BoxDecoration(
      // borderRadius: BorderRadius.circular(12.w),
    shape: BoxShape.circle,
      image: DecorationImage(
        image: NetworkImage('xxxxxxxxx'),
          fit: BoxFit.cover)
      )
    ),
  )
  // border属性: strokeAlign: BorderSide.strokeAlignCenter,确定边框是在哪个位置,内边框/外边框/居中边框;

7.日历控件的使用

SfDateRangePicker

// 注意事项:日历本身属性更改才会刷新
// 解决方法:headerHeight: headerHeight,设置该属性,赋值为headerHeight+/-0.01,
// onViewChange方法内的数据更改需要在Future内,不然页面会报错,如下
  void _onViewChange(dateRangePickerViewChangedArgs) {
    Future.microtask(() {
      setState(() {
        xx = xxx;
      });
    });
  }

8.数组map遍历方法

// 遍历值
list.map((item) => {
   return item;
}).toList();

// 遍历索引
list.asMap().keys.map((index) => {
   return list[index]['xxx'];
}).toList();

9.window问题,不能通过window获取屏幕相关属性,获取页面宽度用以下代替

MediaQuery.of(context).size.width

10.字符串的一些方法

判断是否包含某字符串:str.contains('xx');
截取字符串(从右往左截取两个字符):str.substring(0, str.length - 2);
......

11.滑块的使用及注意事项

Container(
  margin:xxx,
  width: xxx,
  height: 20, // 这里是可点击拖拽滑动的区域,不能跟轨道高度一样小,不然会导致滑动非常不灵敏
  child: SliderTheme(
    data: SliderThemeData(
      trackHeight: 6, // 轨道高度
      xxxxxx
    ),
    child: Slider(
      value: xxx,
      onChanged: (value) {
        setState(() {
          xxx = xxxx;
        });
      },
      onChangeEnd: (value) {
        setState(() {
          xxx = xxxx;
        });
      },
      // 轨道值范围
      min: 0, // 开始值
      max: 5,  // 结束值
      // divisions: 5, // 轨道要分几段
      activeColor: HexColor('xxx'), // 滑动过的颜色
      inactiveColor: HexColor('xxx'), // 未滑动过的颜色
    ),
  ),
)

12.拉起相机拍照或者获取相册获取照片

需要用到插件:image_picker
测试:web端、Android端

import 'dart:io'; // 文件操作用到
import 'package:image_picker/image_picker.dart';
  var imgPath;
// 调取相机或者相册
void _takePhoto() async {
    var img = await ImagePicker().pickImage(source: ImageSource.camera); // 拉起相册:ImageSource.gallery
    setState(() {
      imgPath = File(img!.path);
    });
}
// 将图片展示
Image.network( // android端不支持
    imgPath.path,
),
Image.file( // web端不支持
    imgPath,
),
// 保存图片到本地用到的插件
import 'package:gallery_saver/gallery_saver.dart';
import "package:universal_html/html.dart" as html;
// 保存图片到本地
void _savePhoto() async {
  // 测试gif动图下载后为png格式
    if (kIsWeb) { // web端,下载完之后需要用户保存到手机
    // 方法一,file文件
      final a = html.AnchorElement(href: imgPath.path);
      a.download = '图片.png';
      a.click();
      a.remove();
    // 方法二,base64
  Uint8List bytes = base64.decode(imgBase64);
    final blob = html.Blob([bytes], 'image/jpeg');
      final url = html.Url.createObjectUrlFromBlob(blob);
      final a = html.AnchorElement(href: url);
      a.download = '图片.png';
      a.click();
      a.remove();
      html.Url.revokeObjectUrl(url);
      Fluttertoast.showToast(
        msg: '保存成功!',
      );
    } else {
      GallerySaver.saveImage(File(imgPath!.path).path).then((value) {
        Fluttertoast.showToast(
          msg: '保存成功!',
        );
      });
    }
}

13.使用动画的一些注意事项

  late final AnimationController _repeatController;
  // 线性动画
  late final Animation<double> _animation;
  @override
  void initState() {
    super.initState();
    _repeatController = AnimationController(
        duration: const Duration(microseconds: 2000), vsync: this)
      ..addListener(() {});
    // 动画持续时间是 3秒,此处的this指 TickerProviderStateMixin或SingleTickerProviderStateMixin 
    _repeatController = AnimationController(
      duration: const Duration(seconds: 1),
      vsync: this,
    )..repeat(); // 设置动画重复播放
    // 创建一个从0到360弧度的补间动画 v * 2 * π
    _animation = Tween<double>(begin: 0, end: 1).animate(_repeatController);
  }
class _xxxState extends State<xxx>
    with SingleTickerProviderStateMixin {
    ...
    RotationTransition(
         turns: _animation,
         child: xxx,
      )
}

14.上下固定,中间滚动的布局

Scaffold(
  backgroundColor: HexColor('#F7F7F7'),
  body: Column(
    children: [
      Expanded(
        child: SingleChildScrollView(
          child: Column(children: []),
        )),
        XXX(children: []) // 底部固定
]));

15.路由跳转及获取路由传参

// 跳转,带参
Navigator.push(
    context,
    MaterialPageRoute(
        maintainState: true,
        builder: (_) {
           return getRouter("路由")({
              "xxx": xxxxx, // 参数
           });
}));

// 获取参数
@override
void initState() {
   super.initState();
   print('当前路由参数是:${widget.params}');
}
// 返回上一级路由
  Navigator.of(context).pop();
  Navigator.of(context).pop({"xx":"xxxx"}); // 返回前可以传参数给上一级

16.子控件撑满父控件的高度

IntrinsicHeight(
   child: Row(
      mainAxisSize: MainAxisSize.min,
      children: [Expanded(child:
      Stack(children:[
        子控件1// 有高度,撑开父控件
        子控件2// 与父控件高度一致
    ]))]
   )
)

17.图片的一些使用

// base64图片
Image.memory(
    base64Decode(base64imgStr),
);

// file图片
Image.network( // android端不支持
    imgPath.path,
);
Image.file( // web端不支持
    imgPath,
);

// 工程内静态图片
Image.asset(
  'resource/images/...',
  width: 100,
  fit: BoxFit.fitWidth, // 图片根据宽度高度自适应
);

18.渐变字

ShaderMask(
   shaderCallback: (bounds) => LinearGradient(
       begin: Alignment.centerLeft,
       end: Alignment.centerRight,
       colors: [HexColor(渐变色码1), HexColor(渐变色码2)])
          .createShader(
              Rect.fromLTWH(0, 0, bounds.width, bounds.height),
            ),
       child: Text(
          '渐变字',
          style: TextStyle(
             color: Colors.white,
             fontSize: 18,
          ),
       ),
);

19.弹窗

// 其中一种写法,AlertDialog实现
void showModalFun(BuildContext context) {
  showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          // 去掉默认样式,背景和padding
          backgroundColor: const Color.fromRGBO(0, 0, 0, 0),
          contentPadding: EdgeInsets.zero,
          content: Container() // 弹窗
        );
      });
}

// 打开弹窗
showModalFun(context);

// 关闭弹窗
Navigator.of(context).pop();
// 另一种方法,Dialog实现,宽高不受限的写法
showDialog(
      context: context,
      builder: (context) {
        return StatefulBuilder(builder: (context, setState) {
          return UnconstrainedBox(
              // 抵消弹窗原有的约束,再自己设置宽度
              constrainedAxis: Axis.vertical,
              child: SizedBox(
                  width: MediaQuery.of(context).size.width,
                  child: Dialog(
                      // 去掉默认样式
                      backgroundColor: const Color.fromRGBO(0, 0, 0, 0),
                      insetPadding: EdgeInsets.zero,
                      child: xxxx))); // 真正的弹窗 
      });
});

20.路由相关

// 回到第一个路由(打开的首页)
Navigator.of(context).popUntil((route) => route.isFirst);

// 带参数路由
Navigator.push(
  context,
  MaterialPageRoute(
    maintainState: true,
    builder: (_) {
      return getRouter("/xxx/xxx")(
        {"a": xxx, "b": xxx});
}));

// 带回调的路由
() async {
   await Navigator.push(
     context,
     MaterialPageRoute(
       maintainState: true,
       builder: (_) {
         return getRouter("/xxx/xxx")(
           {"a": xxx, "b": xxx});
   }));
   // 路由跳转结束回到页面后的操作
   // ...
}

// 回到上一路由,也可用于关闭弹窗
Navigator.of(context).pop();

21.以页面高度自适应,页面高度不够展示给一个最小高度页面可滚动

SingleChildScrollView(
   child: Stack(
      alignment: Alignment.topCenter,
      children: [
         Container(
            width: MediaQuery.of(context).size.width,
            height: MediaQuery.of(context).size.height, // 拿页面高度
            constraints: BoxConstraints(minHeight: 600.w), // 页面高度低于600赋值600, 不够展示了就只能滚动展示
          ),
          Positioned(top: 100.w, bottom: 60.w, child: content()) // 需要自适应高度的控件
      ],
));

未完待续...