从类型名称动态创建实例
Dynamically create instance from type name
有一个问题 – 如果我有包含其名称的字符串变量,我可以创建 class 实例吗?
比如我有
var className = 'DocumentsList';
我可以做这样的事情吗
var docListWidget = createInstance(className[, params]);
Flutter(和 Dart)没有这样做的能力。故意。他们实现了 tree shaking,这是一种删除未使用代码的机制,可以让你的应用程序更小、更快。但是,要做到这一点,编译器必须知道 使用了哪些代码,没有使用哪些代码。如果你能像你描述的那样做,它就不可能知道使用了什么代码。
所以不,这是不可能的。没有那种自由度。如果您事先知道它将是哪个字符串,则可以使用一个大的 switch 语句来创建基于字符串的 classes。那是静态的,编译器可以使用它。
你想要的是所谓的“反射”,你可以使用像 reflectable or mirror 这样的包来添加一些功能,但是它们不能改变编译过程,它们也会通过你事先指定的事实来工作 classes 需要反思。完全动态的使用是不可能的(故意的)。
由于您提到了路由 table:您不能从字符串创建 class,但是您可以从 class:
创建字符串
import 'package:flutter/material.dart';
typedef Builder<T> = T Function(BuildContext context);
String routeName<T extends Widget>() {
return T.toString().toLowerCase();
}
MapEntry<String, Builder<Widget>> createRouteWithName<T extends Widget>(Builder<T> builder) {
return new MapEntry(routeName<T>(), (context) => builder(context));
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: routeName<ScreenPicker>(),
routes: Map.fromEntries([
createRouteWithName((context) => ScreenPicker()),
createRouteWithName((context) => ScreenOne()),
createRouteWithName((context) => ScreenTwo()),
]),
);
}
}
class ScreenPicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("Navigate a route")),
body: Column(children: [
RaisedButton(
child: Text('One'),
onPressed: () => Navigator.pushNamed(context, routeName<ScreenOne>())),
RaisedButton(
child: Text('Two'),
onPressed: () => Navigator.pushNamed(context, routeName<ScreenTwo>())),
]));
}
}
class ScreenTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("Second Screen")), body: Center(child: Text("Two")));
}
}
class ScreenOne extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("First Screen")), body: Center(child: Text("One")));
}
}
这样一来,您的项目中就没有带有路由名称的字符串,在重命名某些内容时可能会被更改、输入错误或忘记。
有一个问题 – 如果我有包含其名称的字符串变量,我可以创建 class 实例吗?
比如我有
var className = 'DocumentsList';
我可以做这样的事情吗
var docListWidget = createInstance(className[, params]);
Flutter(和 Dart)没有这样做的能力。故意。他们实现了 tree shaking,这是一种删除未使用代码的机制,可以让你的应用程序更小、更快。但是,要做到这一点,编译器必须知道 使用了哪些代码,没有使用哪些代码。如果你能像你描述的那样做,它就不可能知道使用了什么代码。
所以不,这是不可能的。没有那种自由度。如果您事先知道它将是哪个字符串,则可以使用一个大的 switch 语句来创建基于字符串的 classes。那是静态的,编译器可以使用它。
你想要的是所谓的“反射”,你可以使用像 reflectable or mirror 这样的包来添加一些功能,但是它们不能改变编译过程,它们也会通过你事先指定的事实来工作 classes 需要反思。完全动态的使用是不可能的(故意的)。
由于您提到了路由 table:您不能从字符串创建 class,但是您可以从 class:
创建字符串import 'package:flutter/material.dart';
typedef Builder<T> = T Function(BuildContext context);
String routeName<T extends Widget>() {
return T.toString().toLowerCase();
}
MapEntry<String, Builder<Widget>> createRouteWithName<T extends Widget>(Builder<T> builder) {
return new MapEntry(routeName<T>(), (context) => builder(context));
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: routeName<ScreenPicker>(),
routes: Map.fromEntries([
createRouteWithName((context) => ScreenPicker()),
createRouteWithName((context) => ScreenOne()),
createRouteWithName((context) => ScreenTwo()),
]),
);
}
}
class ScreenPicker extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("Navigate a route")),
body: Column(children: [
RaisedButton(
child: Text('One'),
onPressed: () => Navigator.pushNamed(context, routeName<ScreenOne>())),
RaisedButton(
child: Text('Two'),
onPressed: () => Navigator.pushNamed(context, routeName<ScreenTwo>())),
]));
}
}
class ScreenTwo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("Second Screen")), body: Center(child: Text("Two")));
}
}
class ScreenOne extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("First Screen")), body: Center(child: Text("One")));
}
}
这样一来,您的项目中就没有带有路由名称的字符串,在重命名某些内容时可能会被更改、输入错误或忘记。