学习Flutter的第十一天

6.2 屏幕宽高

6.2.1 系统提供的方法

需要在有context容器中才能使用,也就是build方法中使用

Widget build(BuildContext context){
    final size =MediaQuery.of(context).size;
    final width =size.width;
    final height =size.height; 
}

6.2.2 使用起来最简单的方法

需要提前定义,要不然拿不到,方法报错

import 'dart:ui';

final width = window.physicalSize.width;
final height = window.physicalSize.height;

6.3去掉debug标签

import 'package:flutter/material.dart';
import './pages/tabs.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
    );
  }
}

6.4 PreferredSize

此控件不对其子控件施加任何约束,并且不以任何方式影响孩子的布局。

此控件对自定义AppBar.bottom和AppBar非常有用。

PreferredSize 可以改变 appBar 的高度

Scaffold(
      // 通过 PreferredSize 来改变 appBar的高度
      appBar: PreferredSize(
        preferredSize: const Size.fromHeight(50),
        child: AppBar(
          
        ),
      ),
    )

6.5 保存页面状态

参考:https://blog.csdn.net/qq_14876133/article/details/125393405

AutomaticKeepAliveClientMixin缓存组件

AutomaticKeepAlive 的组件的主要作用是将列表项的根 RenderObject 的 keepAlive 按需自动标记 为 true 或 false。为了方便叙述,我们可以认为根 RenderObject 对应的组件就是列表项的根 Widget,代表整个列表项组件,同时我们将列表组件的 Viewport区域 + cacheExtent(预渲染区域)称为加载区域 :

  1. 当 keepAlive 标记为 false 时,如果列表项滑出加载区域时,列表组件将会被销毁。
  2. 当 keepAlive 标记为 true 时,当列表项滑出加载区域后,Viewport 会将列表组件缓存起来;当列表项进入加载区域时,Viewport 从先从缓存中查找是否已经缓存,如果有则直接复用,如果没有则重新创建列表项。

封装组件代码

import 'package:flutter/material.dart';

class KeepAliveWrapper extends StatefulWidget {
  final bool keepAlive;
  final Widget child;

  const KeepAliveWrapper({Key? key, this.keepAlive = true, required this.child})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return _KeepAliveWrapperState();
  }
}

class _KeepAliveWrapperState extends State<KeepAliveWrapper>
    with AutomaticKeepAliveClientMixin {
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return widget.child;
  }

  @override
  void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
    //状态发生变化时调用
    if (oldWidget.keepAlive != widget.keepAlive) {
      //更新KeepAlive状态
      updateKeepAlive();
    }
    super.didUpdateWidget(oldWidget);
  }

  @override
  bool get wantKeepAlive => widget.keepAlive;
}

在使用的时候,只需要把组件,放到 KeepAliveWrapper 的child中即可。

6.6 配置命名路由

routers.dart

import 'package:flutter/material.dart';
import '../pages/Tabs.dart';
import '../pages/search.dart';
import '../pages/first.dart';

Map routes = {
  "/": (context) => const Tabs(),
  "/s": (context, {arguments}) => Search(arguments: arguments),
  "/f": (context) => const FirstPage(),
};

//固定写法
var onGenerateRoute = (RouteSettings settings) {
  // 统一处理
  final String? name = settings.name;
  final Function? pageContentBuilder = routes[name];
  if (pageContentBuilder != null) {
    if (settings.arguments != null) {
      final Route route = MaterialPageRoute(
          builder: (context) =>
              pageContentBuilder(context, arguments: settings.arguments));
      return route;
    } else {
      final Route route =
          MaterialPageRoute(builder: (context) => pageContentBuilder(context));
      return route;
    }
  }
};

main.dart

import 'package:flutter/material.dart';
import './tools/routers.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      theme: ThemeData(primarySwatch: Colors.blue),
      // home: const Tabs(),
      initialRoute: "/",
      onGenerateRoute: onGenerateRoute,
    );
  }
}

传值

import 'package:flutter/material.dart';

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

  @override
  State<Message> createState() => _MessageState();
}

class _MessageState extends State<Message> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, "/s",
                    arguments: {"title": "我是标题", "content": "我是内容"});
              },
              child: const Text("跳转搜索页面"))
        ],
      ),
    );
  }
}

接收值

import 'package:flutter/material.dart';

class Search extends StatefulWidget {
  final Map arguments;
  const Search({super.key, required this.arguments});

  @override
  State<Search> createState() => _SearchState();
}

class _SearchState extends State<Search> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.arguments["title"]),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text(widget.arguments["content"]),
          const SizedBox(height: 20),
          ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: const Text("返回上一页"))
        ],
      ),
    );
  }
}

6.7 fluttertoast

1、打开网站 https://pub.dev/

2、搜索 fluttertoast

3、在pubspec.yaml中,的 dependencies 中,配置 fluttertoast: ^8.0.9

4、导入包:import 'package:fluttertoast/fluttertoast.dart';

5、使用
Fluttertoast.showToast(
        msg: "提示信息",
        toastLength: Toast.LENGTH_SHORT,    // 针对android平台
        gravity: ToastGravity.CENTER,   // 方位
        timeInSecForIosWeb: 1,  // 提示时间
        backgroundColor: Colors.red, // 背景颜色
        textColor: Colors.white,    // 文字颜色
        fontSize: 16.0  // 字号
    );

6.8 定时器

引入包:

import 'dart:async';


var t = Timer.periodic(const Duration(seconds: 3), (timer) {
  print("定时执行:" + DateTime.now().toString());
  // 取消定时器
  // timer.cancel;
});

// 组件销毁的时候,取消定时器
void dispose() {
  super.dispose();
  t.cancel();
}

6.9 key

key是用来作为WidgetElementSemanticsNode的标示,仅仅用来更新widget->key相同的小部件的状态。

主要用于组件的排序,例如listview中,拖动组件,横竖屏切换

当你想要跨widget树保留状态时 , 应该使用key.

一、LocalKey有三种类型,用作diff算法的核心所在,用Element和widget进行比较

  • ValueKey 以一个数据作为Key。如:数字、字符
  • ObjectKey 以Object对象作为Key
  • UniqueKey 可以保证Key的唯一性!(一旦使用Uniquekey那么就不存在Element复用 了!)

二、GlobalKey

  • 1、GlobalKey可以获取到对应的Widget的State对象!

需求:当我们页面内容很多时,而需要改变的内容只有很少的一部分且在树的底层的时候,我们如何去实现增量更新?

通常情况下有两种方式,

  • 第一种是通过方法的回调,去实现数据更新,
  • 第二种是通过GlobalKey,在StatelessWidget引用StatefulWidget。
//在statelessWidget中引用statefulWidget更新UI
class GlobalKeyDemo extends StatelessWidget{
  // 1、 定义GlobalKey
  final GlobalKey<_ChildPageState> _globalKey = GlobalKey();

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('GlobalDemo'),
      ),
      // 2、给子控件设置key
      body: ChildPage(
        key: _globalKey,
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.add),
        onPressed: (){
         // 3、可以通过 globalKey.currentState 来获取子组件的属性和方法
          _globalKey.currentState.data = 'old' + _globalKey.currentState.count.toString();
          _globalKey.currentState.count++;
          _globalKey.currentState.setState(() { });
        },
      ),
    );
  }

}

class ChildPage extends StatefulWidget{
  ChildPage({Key key}):super(key:key);

  @override
  _ChildPageState createState() => _ChildPageState();
}

class _ChildPageState extends State<ChildPage>{
  int count = 0;
  String data = 'hello Flutter';

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Center(
      child: Column(
        children: <Widget>[
          Text(count.toString()),
          Text(data),
        ],
      ),
    );
  }
}

6.10 Matrix4

参考:https://blog.csdn.net/liu__520/article/details/83796784

  1. scale:缩放比例 transform: Matrix4.diagonal3Values(2, 10, 1),
  2. transform: 移动
  3. rotationZ:绕Z轴旋转
  4. rotationX:绕X轴旋转
  5. rotationY:绕Y轴旋转
  6. columns:设置一个新的矩阵
  7. compose:复合平移、旋转、缩放,形成新的状态
  8. copy:复制一个4*4的张量(矩阵)
  9. identity:恢复初始状态,也就是4*4的单位矩阵
  10. inverted:取相反的矩阵,就是反着来
  11. outer(合并)、skew(扭曲)、skewX(x轴扭曲)、skewY(y轴扭曲)、zero(置零矩阵)、fromList(将一个16位的一维数组转换成4*4的矩阵)

常用的如下:

6.10.1 缩放

Matrix4.diagonal3Values(1, 1, 1)

表示缩放的比例,分别沿x,y,z三个方向,x轴正向向右,y轴正向向下,z轴正向从屏幕朝上,正值表示正向,>1表示放大,小于1大于0表示缩小,负值表示反向。

6.10.2 移动

Matrix4.translationValues

表示平移的距离,分别沿x,y,z三个方向,
x轴正向向右,
y轴正向向下,
z轴正向从屏幕朝上,
正值表示正向移动,负值表示负向移动,
其中z轴移动在平面上无法看出小错

6.10.3 旋转

Matrix4.rotationZ(pi / 6)

绕着Z轴旋转,正向是顺时针,负向是逆时针,
正向也就是从x轴正向往y轴正向旋转

Matrix4.rotationX(pi / 6)

绕着X轴旋转,正向是顺时针,负向是逆时针,
正向也就是从y轴正向往z轴正向旋转

Matrix4.rotationY(pi / 6)

绕着Y轴旋转,正向是顺时针,负向是逆时针,
正向也就是从x轴正向往z轴正向旋转

6.11 photo_view

图片预览插件

photo_view: ^0.13.0

简单使用

  Widget _buildPhotoView() {
    _galleryItems = [
      'assets/images/icon_avatar_staff.png',
      'assets/images/icon_avatar_staff.png',
      'assets/images/icon_avatar_staff.png'
    ];
    return PhotoViewGallery.builder(
      scrollPhysics: const BouncingScrollPhysics(),
      builder: (BuildContext context, int index) {
        return PhotoViewGalleryPageOptions(
          imageProvider: AssetImage(_galleryItems[index]),
          initialScale: PhotoViewComputedScale.contained * 0.9,
        );
      },
      itemCount: _galleryItems.length,
      backgroundDecoration: const BoxDecoration(color: Colors.white),
      pageController: PageController(initialPage: _currentPageIndex),
      onPageChanged: (i) {
        _currentPageIndex = i;
        _valueNotifier.notifyListeners();
      },
    );
  }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容