Flutter基础(纯新手笔记)

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();
 

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()) // 需要自适应高度的控件
      ],
));

 

posted @ 2023-08-31 15:42  桃李子  阅读(170)  评论(0编辑  收藏  举报