升级指南
v7 新特性
Nylo Website v7 是一个重大版本更新,为开发者体验带来了显著改进:
加密环境配置
- 环境变量现在在构建时进行 XOR 加密以提高安全性
- 新增
metro make:key命令生成您的 APP_KEY - 新增
metro make:env命令生成加密的env.g.dart - 支持通过
--dart-define注入 APP_KEY 以适配 CI/CD 流水线
简化的启动流程
- 新的
BootConfig模式取代了分离的 setup/finished 回调 - 更简洁的
Nylo.init()带有env参数用于加密环境 - 应用生命周期钩子直接在 main.dart 中配置
新的 nylo.configure() API
- 单一方法整合所有应用配置
- 更简洁的语法取代单独的
nylo.add*()调用 - provider 中分离的
setup()和boot()生命周期方法
用于 Pages 的 NyPage
NyPage取代NyState用于页面 widget(语法更简洁)view()取代build()方法get init =>getter 取代init()和boot()方法NyState仍可用于非页面的 stateful widget
LoadingStyle 系统
- 新的
LoadingStyle枚举用于一致的加载状态 - 选项:
LoadingStyle.normal()、LoadingStyle.skeletonizer()、LoadingStyle.none() - 通过
LoadingStyle.normal(child: ...)自定义加载 widget
RouteView 类型安全路由
static RouteView path取代static const path- 带有 widget 工厂的类型安全路由定义
多 Theme 支持
- 注册多个深色和浅色 theme
- Theme ID 在代码中定义而非
.env文件 - 新增
NyThemeType.dark/NyThemeType.light用于 theme 分类 - 首选 theme API:
NyTheme.setPreferredDark()、NyTheme.setPreferredLight() - Theme 枚举:
NyTheme.lightThemes()、NyTheme.darkThemes()、NyTheme.all()
新的 Metro 命令
make:key- 生成用于加密的 APP_KEYmake:env- 生成加密的环境文件make:bottom_sheet_modal- 创建底部弹出模态框make:button- 创建自定义按钮
破坏性变更概述
| 变更 | v6 | v7 |
|---|---|---|
| 应用根 Widget | LocalizedApp(child: Main(nylo)) |
Main(nylo)(使用 NyApp.materialApp()) |
| 页面 State 类 | NyState |
NyPage 用于页面 |
| 视图方法 | build() |
view() |
| 初始化方法 | init() async {} / boot() async {} |
get init => () async {} |
| Route 路径 | static const path = '/home' |
static RouteView path = ('/home', (_) => HomePage()) |
| Provider 启动 | boot(Nylo nylo) |
setup(Nylo nylo) + boot(Nylo nylo) |
| 配置方式 | 单独的 nylo.add*() 调用 |
单一 nylo.configure() 调用 |
| Theme ID | .env 文件(LIGHT_THEME_ID、DARK_THEME_ID) |
代码中(type: NyThemeType.dark) |
| 加载 Widget | useSkeletonizer + loading() |
LoadingStyle getter |
| 配置位置 | lib/config/ |
lib/bootstrap/(decoders、events、providers、theme) |
| 资源位置 | public/ |
assets/ |
推荐的迁移方法
对于较大的项目,我们建议创建一个全新的 v7 项目并迁移文件:
- 创建新的 v7 项目:
git clone https://github.com/nylo-core/nylo.git my_app_v7 -b 7.x - 复制您的 pages、controllers、models 和 services
- 按上述说明更新语法
- 彻底测试
这确保您拥有所有最新的样板结构和配置。
如果您想查看 v6 和 v7 之间的变更差异,可以在 GitHub 上查看对比:https://github.com/nylo-core/nylo/compare/6.x...7.x
快速迁移清单
使用此清单跟踪您的迁移进度:
- 更新
pubspec.yaml(Dart >=3.10.7、Flutter >=3.24.0、nylo_framework: ^7.0.0) - 运行
flutter pub get - 运行
metro make:key生成 APP_KEY - 运行
metro make:env生成加密的环境配置 - 使用 env 参数和 BootConfig 更新
main.dart - 将
Boot类转换为使用BootConfig模式 - 将配置文件从
lib/config/移动到lib/bootstrap/ - 创建新的配置文件(
lib/config/app.dart、lib/config/storage_keys.dart、lib/config/toast_notification.dart) - 更新
AppProvider以使用nylo.configure() - 从
.env中移除LIGHT_THEME_ID和DARK_THEME_ID - 为深色 theme 配置添加
type: NyThemeType.dark - 将所有页面 widget 的
NyState重命名为NyPage - 将所有页面中的
build()改为view() - 将所有页面中的
init()/boot()改为get init => - 将
static const path更新为static RouteView path - 在路由中将
router.route()改为router.add() - 重命名 widget(NyListView -> CollectionView 等)
- 将资源从
public/移动到assets/ - 更新
pubspec.yaml中的资源路径 - 移除 Firebase 导入(如果使用 - 直接添加包)
- 移除 NyDevPanel 使用(改用 Flutter DevTools)
- 运行
flutter pub get并测试
分步迁移指南
步骤 1:更新依赖
更新您的 pubspec.yaml:
environment:
sdk: '>=3.10.7 <4.0.0'
flutter: ">=3.24.0"
dependencies:
nylo_framework: ^7.0.0
# ... 其他依赖
运行 flutter pub get 更新包。
步骤 2:环境配置
v7 需要加密的环境变量以提高安全性。
1. 生成 APP_KEY:
metro make:key
这会将 APP_KEY 添加到您的 .env 文件。
2. 生成加密的 env.g.dart:
metro make:env
这会创建 lib/bootstrap/env.g.dart,包含您的加密环境变量。
3. 从 .env 中移除已弃用的 theme 变量:
# 从您的 .env 文件中移除这些行:
LIGHT_THEME_ID=...
DARK_THEME_ID=...
步骤 3:更新 main.dart
v6:
import 'package:nylo_framework/nylo_framework.dart';
import 'bootstrap/boot.dart';
void main() async {
await Nylo.init(
setup: Boot.nylo,
setupFinished: Boot.finished,
);
}
v7:
import '/bootstrap/env.g.dart';
import 'package:nylo_framework/nylo_framework.dart';
import 'bootstrap/boot.dart';
void main() async {
await Nylo.init(
env: Env.get,
setup: Boot.nylo(),
appLifecycle: {
// 可选:添加应用生命周期钩子
// AppLifecycleState.resumed: () => print("App resumed"),
// AppLifecycleState.paused: () => print("App paused"),
},
);
}
主要变更:
- 导入生成的
env.g.dart - 将
Env.get传递给env参数 Boot.nylo现在是Boot.nylo()(返回BootConfig)setupFinished已移除(在BootConfig内部处理)- 可选的
appLifecycle钩子用于应用状态变化
步骤 4:更新 boot.dart
v6:
class Boot {
static Future<Nylo> nylo() async {
WidgetsFlutterBinding.ensureInitialized();
if (getEnv('SHOW_SPLASH_SCREEN', defaultValue: false)) {
runApp(SplashScreen.app());
}
await _setup();
return await bootApplication(providers);
}
static Future<void> finished(Nylo nylo) async {
await bootFinished(nylo, providers);
runApp(Main(nylo));
}
}
v7:
class Boot {
static BootConfig nylo() => BootConfig(
setup: () async {
WidgetsFlutterBinding.ensureInitialized();
if (AppConfig.showSplashScreen) {
runApp(SplashScreen.app());
}
await _init();
return await setupApplication(providers);
},
boot: (Nylo nylo) async {
await bootFinished(nylo, providers);
runApp(Main(nylo));
},
);
}
主要变更:
- 返回
BootConfig而非Future<Nylo> setup和finished合并为单一BootConfig对象getEnv('SHOW_SPLASH_SCREEN')->AppConfig.showSplashScreenbootApplication->setupApplication
步骤 5:重组配置文件
v7 重组了配置文件以获得更好的结构:
| v6 位置 | v7 位置 | 操作 |
|---|---|---|
lib/config/decoders.dart |
lib/bootstrap/decoders.dart |
移动 |
lib/config/events.dart |
lib/bootstrap/events.dart |
移动 |
lib/config/providers.dart |
lib/bootstrap/providers.dart |
移动 |
lib/config/theme.dart |
lib/bootstrap/theme.dart |
移动 |
lib/config/keys.dart |
lib/config/storage_keys.dart |
重命名并重构 |
| (新建) | lib/config/app.dart |
创建 |
| (新建) | lib/config/toast_notification.dart |
创建 |
创建 lib/config/app.dart:
参考:App Config
class AppConfig {
// 应用名称。
static final String appName = getEnv('APP_NAME', defaultValue: 'Nylo');
// 应用版本。
static final String version = getEnv('APP_VERSION', defaultValue: '1.0.0');
// 在此添加其他应用配置
}
创建 lib/config/storage_keys.dart:
参考:Storage Keys
final class StorageKeysConfig {
// 定义需要在启动时同步的键
static syncedOnBoot() => () async {
return [
auth,
bearerToken,
// coins.defaultValue(10), // 默认给用户 10 个金币
];
};
static StorageKey auth = 'SK_USER';
static StorageKey bearerToken = 'SK_BEARER_TOKEN';
// static StorageKey coins = 'SK_COINS';
/// 在此添加您的 storage key...
}
创建 lib/config/toast_notification.dart:
import 'package:nylo_framework/nylo_framework.dart';
class ToastNotificationConfig {
static Map<ToastNotificationStyleMetaHelper, ToastMeta> styles = {
// 在此自定义 toast 样式
};
}
步骤 6:更新 AppProvider
v6:
class AppProvider implements NyProvider {
@override
boot(Nylo nylo) async {
await NyLocalization.instance.init(
localeType: localeType,
languageCode: languageCode,
assetsDirectory: assetsDirectory,
);
nylo.addLoader(loader);
nylo.addLogo(logo);
nylo.addThemes(appThemes);
nylo.addToastNotification(getToastNotificationWidget);
nylo.addValidationRules(validationRules);
nylo.addModelDecoders(modelDecoders);
nylo.addControllers(controllers);
nylo.addApiDecoders(apiDecoders);
nylo.useErrorStack();
nylo.addAuthKey(Keys.auth);
await nylo.syncKeys(Keys.syncedOnBoot);
return nylo;
}
@override
afterBoot(Nylo nylo) async {}
}
v7:
class AppProvider implements NyProvider {
@override
setup(Nylo nylo) async {
await nylo.configure(
localization: NyLocalizationConfig(
languageCode: LocalizationConfig.languageCode,
localeType: LocalizationConfig.localeType,
assetsDirectory: LocalizationConfig.assetsDirectory,
),
loader: DesignConfig.loader,
logo: DesignConfig.logo,
themes: appThemes,
initialThemeId: 'light_theme',
toastNotifications: ToastNotificationConfig.styles,
modelDecoders: modelDecoders,
controllers: controllers,
apiDecoders: apiDecoders,
authKey: StorageKeysConfig.auth,
syncKeys: StorageKeysConfig.syncedOnBoot,
useErrorStack: true,
);
return nylo;
}
@override
boot(Nylo nylo) async {}
}
主要变更:
boot()现在是setup()用于初始配置boot()现在用于 setup 后的逻辑(之前是afterBoot)- 所有
nylo.add*()调用整合为单一nylo.configure() - 本地化使用
NyLocalizationConfig对象
步骤 7:更新 Theme 配置
v6(.env 文件):
LIGHT_THEME_ID=default_light_theme
DARK_THEME_ID=default_dark_theme
v6(theme.dart):
final List<BaseThemeConfig> appThemes = [
BaseThemeConfig(
id: getEnv('LIGHT_THEME_ID'),
description: "Light Theme",
theme: lightTheme(),
colors: LightThemeColors(),
),
BaseThemeConfig(
id: getEnv('DARK_THEME_ID'),
description: "Dark Theme",
theme: darkTheme(),
colors: DarkThemeColors(),
),
];
v7(theme.dart):
final List<BaseThemeConfig<ColorStyles>> appThemes = [
BaseThemeConfig<ColorStyles>(
id: 'light_theme',
theme: lightTheme,
colors: LightThemeColors(),
type: NyThemeType.light,
),
BaseThemeConfig<ColorStyles>(
id: 'dark_theme',
theme: darkTheme,
colors: DarkThemeColors(),
type: NyThemeType.dark,
),
];
主要变更:
- 从
.env中移除LIGHT_THEME_ID和DARK_THEME_ID - 直接在代码中定义 theme ID
- 为所有深色 theme 配置添加
type: NyThemeType.dark - 浅色 theme 默认为
NyThemeType.light
新的 Theme API 方法(v7):
// 设置并记住首选 theme
NyTheme.set(context, id: 'dark_theme', remember: true);
// 设置系统跟随的首选 theme
NyTheme.setPreferredDark('dark_theme');
NyTheme.setPreferredLight('light_theme');
// 获取首选 theme ID
String? darkId = NyTheme.preferredDarkId();
String? lightId = NyTheme.preferredLightId();
// Theme 枚举
List<BaseThemeConfig> lights = NyTheme.lightThemes();
List<BaseThemeConfig> darks = NyTheme.darkThemes();
List<BaseThemeConfig> all = NyTheme.all();
BaseThemeConfig? theme = NyTheme.getById('dark_theme');
List<BaseThemeConfig> byType = NyTheme.getByType(NyThemeType.dark);
// 清除保存的偏好设置
NyTheme.clearSavedTheme();
步骤 10:迁移 Widgets
NyListView -> CollectionView
v6:
NyListView(
child: (context, data) {
return ListTile(title: Text(data.name));
},
data: () async => await api.getUsers(),
loading: CircularProgressIndicator(),
)
v7:
CollectionView<User>(
data: () async => await api.getUsers(),
builder: (context, item) => ListTile(
title: Text(item.data.name),
),
loadingStyle: LoadingStyle.normal(),
)
// 带分页(下拉刷新):
CollectionView<User>.pullable(
data: (page) async => await api.getUsers(page: page),
builder: (context, item) => ListTile(
title: Text(item.data.name),
),
)
NyFutureBuilder -> FutureWidget
v6:
NyFutureBuilder(
future: fetchData(),
child: (context, data) => Text(data),
loading: CircularProgressIndicator(),
)
v7:
FutureWidget<String>(
future: fetchData(),
child: (context, data) => Text(data ?? ''),
loadingStyle: LoadingStyle.normal(),
)
NyTextField -> InputField
v6:
NyTextField(
controller: _controller,
validationRules: "not_empty|email",
)
v7:
InputField(
controller: _controller,
formValidator: FormValidator
.notEmpty()
.email(),
),
NyRichText -> StyledText
v6:
NyRichText(children: [
Text("Hello", style: TextStyle(color: Colors.yellow)),
Text(" WORLD ", style: TextStyle(color: Colors.blue)),
Text("!", style: TextStyle(color: Colors.red)),
]),
v7:
StyledText.template(
"{{Hello}} {{WORLD}}{{!}}",
styles: {
"Hello": TextStyle(color: Colors.yellow),
"WORLD": TextStyle(color: Colors.blue),
"!": TextStyle(color: Colors.red),
},
)
NyLanguageSwitcher -> LanguageSwitcher
v6:
NyLanguageSwitcher(
onLanguageChange: (locale) => print(locale),
)
v7:
LanguageSwitcher(
onLanguageChange: (locale) => print(locale),
)
步骤 11:更新资源路径
v7 将资源目录从 public/ 更改为 assets/:
1. 移动您的资源文件夹:
# 移动目录
mv public/fonts assets/fonts
mv public/images assets/images
mv public/app_icon assets/app_icon
2. 更新 pubspec.yaml:
v6:
flutter:
assets:
- public/fonts/
- public/images/
- public/app_icon/
v7:
flutter:
assets:
- assets/fonts/
- assets/images/
- assets/app_icon/
3. 更新代码中的资源引用:
v6:
Image.asset('public/images/logo.png')
v7:
Image.asset('assets/images/logo.png')
LocalizedApp Widget - 已移除
参考:Main Widget
迁移方法: 直接使用 Main(nylo)。NyApp.materialApp() 在内部处理本地化。
v6:
runApp(LocalizedApp(child: Main(nylo)));
v7:
runApp(Main(nylo));
已删除类参考
| 已删除的类 | 替代方案 |
|---|---|
NyTextStyle |
直接使用 Flutter 的 TextStyle |
NyBaseApiService |
使用 DioApiService |
BaseColorStyles |
使用 ThemeColor |
LocalizedApp |
直接使用 Main(nylo) |
NyException |
使用标准 Dart 异常 |
PushNotification |
直接使用 flutter_local_notifications |
PushNotificationAttachments |
直接使用 flutter_local_notifications |
Widget 迁移参考
重命名的 Widgets
| v6 Widget | v7 Widget | 备注 |
|---|---|---|
NyListView |
CollectionView |
新 API 使用 builder 而非 child |
NyFutureBuilder |
FutureWidget |
简化的异步 widget |
NyTextField |
InputField |
使用 FormValidator |
NyLanguageSwitcher |
LanguageSwitcher |
API 相同 |
NyRichText |
StyledText |
API 相同 |
NyFader |
FadeOverlay |
API 相同 |
已删除的 Widgets(无直接替代)
| 已删除的 Widget | 替代方案 |
|---|---|
NyPullToRefresh |
使用 CollectionView.pullable() |
Widget 迁移示例
NyPullToRefresh -> CollectionView.pullable():
v6:
NyPullToRefresh(
child: (context, data) => ListTile(title: Text(data.name)),
data: (page) async => await fetchData(page),
)
v7:
CollectionView<MyModel>.pullable(
data: (page) async => await fetchData(page),
builder: (context, item) => ListTile(title: Text(item.data.name)),
)
NyFader -> FadeOverlay:
v6:
NyFader(
child: MyWidget(),
)
v7:
FadeOverlay.bottom(
child: MyWidget(),
);
故障排除
"Env.get not found" 或 "Env is not defined"
解决方案: 运行环境生成命令:
metro make:key
metro make:env
然后在 main.dart 中导入生成的文件:
import '/bootstrap/env.g.dart';
"Theme not applying" 或 "Dark theme not working"
解决方案: 确保深色 theme 有 type: NyThemeType.dark:
BaseThemeConfig(
id: 'dark_theme',
description: "Dark Theme",
theme: darkTheme(),
colors: DarkThemeColors(),
type: NyThemeType.dark, // 添加这一行
),
"LocalizedApp not found"
参考:Main Widget
解决方案: LocalizedApp 已被移除。更改:
// 从:
runApp(LocalizedApp(child: Main(nylo)));
// 改为:
runApp(Main(nylo));
"router.route is not defined"
解决方案: 使用 router.add() 代替:
// 从:
router.route(HomePage.path, (context) => HomePage());
// 改为:
router.add(HomePage.path);
"NyListView not found"
解决方案: NyListView 现在是 CollectionView:
// 从:
NyListView(...)
// 改为:
CollectionView<MyModel>(...)
资源无法加载(图片、字体)
解决方案: 将资源路径从 public/ 更新为 assets/:
- 移动文件:
mv public/* assets/ - 更新
pubspec.yaml路径 - 更新代码引用
"init() must return a value of type Future"
解决方案: 改为 getter 语法:
// 从:
@override
init() async { ... }
// 改为:
@override
get init => () async { ... };