Flutter 知识集锦 | extension 拓展类方法


theme: cyanosis

Dart 的拓展类方法已经支持很久了,之前忘了写篇文章介绍一下。最近写了几个拓展方法,借此机会,好好介绍一下 extension 关键字对类的拓展。


1. 从密文的字符串开始说起

比如现在有个需求:

将一个长度大于 4 的字符串,仅保留首尾两个字符,进行密文展示。
比如 1981462002 -> 19******02

很容易可以想到,定义一个 hide 方法,输入一个字符串,进行处理后,输出目标字符串。处理逻辑如下:

```dart void main(){ String num = "1981462002"; String ret = hide(num); print(ret); // 19**02 }

String hide(String src){ String p0 = src.substring(0, 2); String p1 = List.filled(src.length - 4, '*').join(); String p2 = src.substring(src.length-2); return "$p0$p1$p2"; } ```

上面通过 hide(num) 来调用方法,获得结果字符串。但是这种全局方法单独放置比较零散,维护起来有点麻烦。Dart 中提供了 extension 关键字拓展类方法,可以为一个类附加额外的方法.

通过 extension [name] on [type] 的语法定义 type 类型的拓展方法。如下所示,拓展 String 类型时,将之前的 hide 逻辑放入其中即可。此时 hide 方法可以访问 String 类中的公开成员和方法:

dart extension TolyStringExt on String { String hide(){ String p0 = substring(0, 2); String p1 = List.filled(length - 4, '*').join(); String p2 = substring(length-2); return "$p0$p1$p2"; } }

这样 hide 成为 String 类型对象的成员方法,其优势在于:

  • [1]. 可以以增强某类型功能为目的,将某个函数收编,提供居住场所,不至于流落在外。
  • [2]. 通过类型访问方法来调用实现功能,语义性更好。IDE 有快捷提示,方便使用。
  • [3]. 一般拓展方法,在书写上更加简洁,拓展的方法,可以共享复用。

image.png

下面对 hide 方法进行优化,支持自定义密文字符,首尾保留的长度,以及字符串长短的校验。你也可以封装其他关于字符串的实用方法,比如一些邮箱、密码正则的校验。

dart extension TolyStringExt on String { String hide({ String fix = "*", int start = 2, int end = 2, }) { if (length <= start) { return this; } String p0 = substring(0, start); int hideCount = length - start - end; if (hideCount <= 0) { return p0.padRight(length, fix); } String p1 = List.filled(length - (start + end), fix).join(); String p2 = substring(length - end); return "$p0$p1$p2"; } }


2. BuildContext 的拓展方法

Flutter 中 BuildContext 是一个非常重要对象,它作为 Element 的顶层接口,负责维护构建过程中的上下文信息,可以通过它来向上层查找元素节点、访问渲染对象。很多状态管理、路由的类库中,都可以看到对它复写的身影。这里以一个简单的 Snack 弹框为例,看一下对 BuildContext 的拓展。

| 成功 | 提醒 | | --- | --- | | c2f0d4fcae9a5ba75fa9e0fff7d8ad0.jpg | c84f28f0238dd77c3bb0490ab90a32c.jpg |

如下所示,这是以前对 Toast 的简单封装,使用静态方法来简化调用,将 BuildContext 作为入参传入其中。并提供三种颜色作为成功、失败、警告三种场景的背景色:

```dart import 'package:flutter/cupertino.dart';

class Toast{ static void success(BuildContext context,String msg){ toast(context, Colors.green, msg); }

static void error(BuildContext context,String msg){ toast(context, Colors.red, msg); }

static void warning(BuildContext context,String msg){ toast(context, Colors.orange, msg); }

static void toast(BuildContext context,Color color,String msg){ ScaffoldMessenger.of(context).showSnackBar(SnackBar( backgroundColor: color, content: Text(msg))); } } ```

方法调用如下:

dart Toast.warning(context,'当前领域秘钥未修改,无需提交');


可以通过拓展 BuildContext 来简化,将 toast 视为 BuildContext 的能力,在调用时将会更简洁:

092ec9243c4cc03f703bfad41b18b02.png

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

extension ToastContext on BuildContext {

void toast(Color color, String msg) { ScaffoldMessenger.of(this).showSnackBar( SnackBar(backgroundColor: color, content: Text(msg)), ); }

void warning(String msg) => toast(Colors.orange, msg);

void error(String msg) => toast(Colors.red, msg);

void success(String msg) => toast(Colors.green, msg); } ```


3. 其他三方库对 BuildContext 的拓展

provider 中的 read、 watch、select 等操作,是对 BuildContext 的拓展,可以更方便地通过上下文调用的方式访问提供器:

```dart extension ReadContext on BuildContext { T read () { return Provider.of (this, listen: false); } }

extension WatchContext on BuildContext { T watch () { return Provider.of (this); } }

extension SelectContext on BuildContext { R select (R Function(T value) selector) {...} }

```

go_router 中的 push、pop、go 等一系列方法,也是对 BuildContext 的拓展,可以更方便的通过 BuildContext 对象触发方法,来操作路由的变化:

image.png


总的来看,拓展方法可以让作为入参的某个对象拥有 主动权,作为该类型的附加方法,可以达到简化调用的目的。不过拓展方法虽好,可不要贪杯哦,肆意的拓展,可能会使代码很难让别人读懂,这点和运算符的重载类似。以语义为准绳,不要为了炫技而覆写或拓展。那本文就到这里,谢谢观看 ~

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的知识付费类的主页设计,包括顶部导航栏、搜索框、分类列表和推荐课程。 ```dart import 'package:flutter/material.dart'; class KnowledgePayPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('知识付费'), actions: [ IconButton( icon: Icon(Icons.search), onPressed: () {}, ) ], ), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: EdgeInsets.all(16), child: Text( '全部分类', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ), Container( height: 100, child: ListView( scrollDirection: Axis.horizontal, children: [ _buildCategoryItem('IT', Icons.computer), _buildCategoryItem('设计', Icons.palette), _buildCategoryItem('语言', Icons.language), _buildCategoryItem('商业', Icons.business), _buildCategoryItem('健康', Icons.favorite), ], ), ), Padding( padding: EdgeInsets.all(16), child: Text( '热门推荐', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ), _buildCourseItem( 'Flutter 从入门到实战', 'https://picsum.photos/200/300', '张三', 4.5, 199, ), _buildCourseItem( 'Python 数据分析与处理', 'https://picsum.photos/200/300', '李四', 4.8, 299, ), _buildCourseItem( '前端开发高级进阶', 'https://picsum.photos/200/300', '王五', 4.6, 399, ), ], ), ), ); } Widget _buildCategoryItem(String title, IconData icon) { return Container( margin: EdgeInsets.symmetric(horizontal: 8), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircleAvatar( child: Icon(icon), ), SizedBox(height: 8), Text(title), ], ), ); } Widget _buildCourseItem( String title, String imageUrl, String author, double rating, int price) { return Container( margin: EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: 120, height: 80, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), image: DecorationImage( image: NetworkImage(imageUrl), fit: BoxFit.cover, ), ), ), SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), SizedBox(height: 8), Text( author, style: TextStyle(color: Colors.grey), ), SizedBox(height: 8), Row( children: [ Icon( Icons.star, color: Colors.orange, size: 16, ), SizedBox(width: 4), Text( rating.toString(), style: TextStyle(color: Colors.grey), ), SizedBox(width: 16), Text( '¥$price', style: TextStyle(fontSize: 16, color: Colors.red), ), ], ), ], ), ), ], ), ); } } ``` 效果如下: ![知识付费主页](https://i.loli.net/2021/09/28/4YERhnzPZoQ8W2G.png)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值