Flutter文本输入效果插件typer的使用
Flutter文本输入效果插件typer的使用
Typer
提供 <code>Typer<X></code>
,一个用户编写但功能更强大的版本的 <code>Type</code>
。
内置的 <code>Type</code>
类
Dart语言内置了通过评估相应的类型字面量作为表达式来获取给定类型 <code>T</code>
的实例化表示的功能:
typedef TypeOf<X> = X;
void main() {
// `type` 是表示类型 `int` 的对象。
Type type = int;
// 有些类型不是表达式,但我们可以通过类型别名如 `TypeOf` 获取其 `<code>Type`
Type functionType = TypeOf<void Function([String])>;
// `<code>Type` 实例不能用于太多事情,但它们确实具有相等性。
print(type == functionType); // 'false'.
// 例如,我们不能在类型测试或子类型检查中使用它们。
1 is type; // 编译错误。
1 as type; // 编译错误。
type <= functionType; // 编译错误。
}
内置 <code>Type</code>
类的最大用途是我们可以使用 <code>runtimeType</code>
获取任何给定对象的运行时类型:
void main() {
Object? o1 = ..., o2 = ...; // 任意对象都可以。
print(o1.runtimeType == o2.runtimeType);
}
Typer
对于获取现有对象的运行时类型以外的所有其他操作,我们可以使用 <code>Typer<T></code>
的实例,其中 <code>T</code>
是我们可以表示的任何类型,这将比 <code>Type</code>
能做更多事情。
比较类型
特别是,<code>Typer</code>
支持关系运算符 <code><</code>
,<code><=</code>
,<code>></code>
和 <code>>=</code>
。它们会确定一个 <code>Typer</code>
表示的类型是否是另一个类型的子类型/超类型:
void main() {
const t1 = Typer<int>();
const t2 = Typer<num>();
print(t1 <= t2); // 'true'.
print(t2 <= t1); // 'false'.
}
类型测试和类型转换
如果 <code>typer</code>
是 <code>Typer<T></code>
的实例,则我们可以使用 <code>o.isA(typer)</code>
测试给定对象 <code>o</code>
是否是 <code>T</code>
的实例,并使用 <code>o.asA(typer)</code>
进行 <code>T</code>
类型的转换。注意这些测试将使用 <code>typer</code>
的实际类型值,而不是静态已知的类型值(可能是实际值的任何超类型)。
void main() {
// 假设我们忘记了 `<code>typer</code>` 表示 `<code>int</code>`,我们只知道它是 `<code>num</code>` 的子类型。
Typer<num> typer = Typer<int>();
// 但是 `<code>isA</code>` (和 `<code>asA</code>`) 方法将使用实际类型。
print(2.isA(typer)); // 'true'.
print(1.5.isA(typer)); // 'false'.
2.asA(typer); // OK.
1.5.asA(typer); // 抛出异常。
}
<code>isA</code>
,<code>isNotA</code>
和 <code>asA</code>
是扩展方法。它们基于实例成员。我们可能希望使用扩展方法,因为它们具有更常规的语法,或者我们可能(或需要)使用实例成员,例如因为我们不想导入扩展,或者因为调用必须是动态的。
// 同样使用实例成员。
void main() {
Typer<num> typer = Typer<int>();
print(typer.containsInstance(2)); // 'true'.
print(typer.containsInstance(1.5)); // 'false'.
typer.cast(2); // OK.
typer.cast(1.5); // 抛出异常。
}
我们可以使用 getter <code>type</code>
访问底层类型作为一个对象(<code>Type</code>
的实例):
void main() {
Typer<num> typer = Typer<int>();
print(typer.type); // 'int'.
}
使用 <code>Typer</code>
表示的类型
我们可以使用方法 <code>callWith</code>
以静态安全的方式访问底层类型(这本质上是一个“存在性打开”操作):
List<X> createList<X>(Typer<X> typer) =>
typer.callWith(<Y>() => <Y>[] as List<X>);
void main() {
// 再次,我们并不完全了解类型。
Typer<num> typer = Typer<int>();
List<num> xs = createList(typer);
print(xs is List<int>); // 'true'.
}
要理解为什么这是一个非平凡的操作,请尝试完成以下示例,该示例使用内置的 <code>Type</code>
对象执行相同的操作:
List<X> createList<X>(Type type) => ...; // 注意!
void main() {
Type type = int;
List<num> xs = createList(type);
print(xs is List<int>); // 'true'.
}
请注意 <code>X</code>
是无约束的,并且没有任何保证它与给定的 <code>Type</code>
有任何关系(<code>X</code>
可能的值为 <code>String</code>
,而 <code>type</code>
可能是 <code>int</code>
的具体表示,我们不会知道有问题)。
实际上,除了使用 'dart:mirrors'
之外,不可能从给定的 <code>type</code>
中提取任何信息(除了相等性,比如可以用来测试 <code>type != X</code>
,但这不是很有用)。因此我们基本上无法编写代码(再次,除非使用镜子)来创建一个 <code>List<int></code>
,基于 <code>type</code>
是 <code>int</code>
的具体表示。使用相等性我们可以迈出一些小步,但它不会扩展:
List<X> createList<X>(Type type) => switch (type) {
int => <int>[],
String => <String>[],
_ => throw "Surely we don't need more cases! ;-)",
};
最后我们有两个与提升相关的函数:
void main() {
Typer<num> typer = Typer<int>();
num n = Random().nextBool() ? 2 : 2.5;
print('Promoting:');
List<num>? xs = typer.promoteOrNull(n, <X extends num>(X promoted) {
print(' The promotion to `typer` succeeded!');
return <X>[promoted];
});
print('Type of `xs`: ${xs.runtimeType}'); // `List<int>` 或 `Null`.
print('Promoting with `orElse` fallback:');
Object o = n; // 从一个相当通用的类型进行提升。
num n2 = typer.promote(o, <X extends num>(X promoted) {
print(' The promotion to `typer` succeeded!');
// `typer` 具有静态类型 `Typer<num>`,所以我们可以在 `num` 成员上使用。
promoted.floor();
return promoted;
},
orElse: () => 14,
);
print('n2: $n2'); // '2' 或 '14'。
}
我们不能直接使用 <code>is</code>
或 <code>as</code>
来获得提升,因为我们不能直接测试给定 <code>Typer<T></code>
的底层类型 <code>T</code>
。我们可以调用 <code>isA</code>
或 <code>asA</code>
,但这些方法不会导致接收者的提升,因为类型系统不知道 <code>isA</code>
实际返回 <code>true</code>
或 <code>false</code>
,就像 <code>is</code>
一样,而且 <code>asA</code>
将抛出异常,尽管是基于类型 <code>T</code>
而不是本地可描述的类型。
然而,我们可以传递一个泛型回调,该回调将接收到 <code>Typer<T></code>
的底层类型 <code>T</code>
作为其实际参数,并且它也将接收到正在类型测试的对象(在示例中为 <code>promoted</code>
)。
在该回调的主体中,我们可以使用提升后的值,且静态已知它具有一个子类型于该类型参数的类型(在示例中:我们知道实际参数的类型为 <code>X</code>
)。请注意 <code>X</code>
受到 <code>typer</code>
静态已知类型参数的限制,这意味着我们可以在提升后的对象上使用 <code>num</code>
接口。
不过,需要注意的是,我们实际上是将提升后的对象提升到 <code>typer</code>
表示的类型(这里是 <code>int</code>
),而不仅仅是提升到静态已知的边界(这里是 <code>num</code>
)。所以当 <code>n</code>
的值为 <code>2.5</code>
时,我们将使用 <code>orElse()</code>
的值,而不运行 <code>callback</code>
。
示例设计
这是一个设计示例,可以启用类似的存在性打开操作,这是一种相当通用的方法。也就是说,它允许我们从外部使用给定对象的类型参数。
基本思想是,一个具有类型参数 <code>X1 .. Xk</code>
的泛型类具有每个类型变量 <code>Xj</code>
的getter,返回 <code>Typer<Xj></code>
。
这些getter可以用于编写使用类的每个类型变量的实际值的代码,这也是Dart不支持客户端代码的一个特性。事实上,只有类体内的代码才能访问类型变量,因为这些类型变量仅在该代码范围内有效。外部客户端只知道一个大致的值,这是基于给定对象的静态已知类型的一个超类型。
例如,在 <code>List<E></code>
类体内的类型参数 <code>E</code>
在范围内,我们可以像 <code>return <E>{};</code>
这样做。但在 <code>List</code>
外部的代码中,我们可能只知道列表是一个 <code>List<T></code>
,其中 <code>T</code>
可能是实际值的任何超类型,例如,我们可能只知道它是一个 <code>List<Object?></code>
。
在这个示例中,我们使用 <code>Typer</code>
从给定的列表创建一个集合,保留实际的类型参数。
这是一个非平凡的壮举,如果你曾经尝试过创建一个具有与现有对象相同类型参数的新对象,或者类似的事情,你可能会有所体会。
该示例使用模拟类 <code>MyIterable</code>
,<code>MyList</code>
和 <code>MySet</code>
。这只是因为我们不能轻易地向真实的 <code>List</code>
和 <code>Set</code>
类添加必要的 <code>Typer</code>
getter。尽管如此,如果添加了这些getter,这些技术也可以应用于真实的 <code>List</code>
和 <code>Set</code>
对象,就像在这里使用的一样。
这适用于任何类,当然:如果你想为其中一个类启用这种类型的存在性打开,你只需要添加那些 <code>Typer</code>
getter,然后就可以使用这些技术。
以下是示例代码:
abstract class MyIterable<E> {
const MyIterable();
E get first;
Typer<E> get typerOfE => Typer();
}
class MyList<E> extends MyIterable<E> {
final E e;
const MyList(this.e);
[@override](/user/override)
E get first => e;
}
class MySet<E> extends MyIterable<E> {
final E e;
const MySet(this.e);
[@override](/user/override)
E get first => e;
}
MySet<X> iterableToSet<X>(MyIterable<X> iterable) =>
iterable.typerOfE.callWith<Y>() {
return MySet<Y>(iterable.first as Y) as MySet<X>;
};
void main() {
// 假设我们不知道 `iterable` 的确切类型。
MyIterable<Object?> iterable = MyList<int>(42);
// 现在我们想创建一个具有相同元素类型的集合。
var set = iterableToSet(iterable);
print(set.runtimeType); // 'MySet<int>'。
}
更多关于Flutter文本输入效果插件typer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter文本输入效果插件typer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用typer
插件来实现文本输入效果的代码示例。typer
插件允许你以类型机(打字机)的效果显示文本,非常适合在应用中创建有趣的文本显示动画。
首先,确保你已经在你的pubspec.yaml
文件中添加了typer
依赖:
dependencies:
flutter:
sdk: flutter
typer: ^x.y.z # 替换为最新的版本号
然后,运行flutter pub get
来安装依赖。
下面是一个完整的Flutter应用示例,展示如何使用typer
插件:
import 'package:flutter/material.dart';
import 'package:typer/typer.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Typer Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TyperController _typerController = TyperController();
@override
void initState() {
super.initState();
// 初始化 TyperController 并设置打字文本
_startTyping();
}
void _startTyping() {
// 文本内容
String textToType = "欢迎使用Flutter Typer插件!";
// 开始打字效果
_typerController.startTyping(
text: textToType,
duration: Duration(seconds: 5), // 打字总时长
onFinish: () {
// 打字完成后的操作
print("打字完成!");
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Typer 示例'),
),
body: Center(
child: TyperAnimatedText(
text: _typerController.text, // 绑定控制器中的文本
style: TextStyle(fontSize: 24, color: Colors.black),
speedFactor: _typerController.speedFactor, // 使用控制器中的速度因子
cursorColor: Colors.blue,
cursorBlink: true,
cursorWidth: 2.0,
showCursor: true,
onTap: () {
// 点击重新开始打字效果
_startTyping();
},
),
),
);
}
}
在这个示例中:
- 我们创建了一个Flutter应用,并在
pubspec.yaml
中添加了typer
依赖。 MyHomePage
是一个有状态的Widget,包含一个TyperController
实例。- 在
initState
方法中,我们调用_startTyping
方法来初始化打字效果。 _startTyping
方法设置了要显示的文本、打字总时长以及打字完成后的回调。- 在
build
方法中,我们使用TyperAnimatedText
来显示打字效果,并绑定到TyperController
的文本和速度因子。 - 用户可以通过点击文本重新开始打字效果。
这个示例展示了如何使用typer
插件来创建一个简单的打字机效果。你可以根据需要调整文本、速度、样式等参数,以符合你的应用需求。