Flutter for OpenHarmony数独游戏App实战:项目初始化与环境配置
本文详细介绍了Flutter for OpenHarmony数独游戏项目的初始化过程。首先通过flutter create创建项目,配置pubspec.yaml添加必要的依赖库。然后添加OpenHarmony平台支持,设置构建配置。项目采用MVC架构,包含控制器、模型和工具类,其中GameController管理游戏状态,PuzzleGenerator生成数独谜题。文章还介绍了屏幕适配配置、调试设
项目初始化是开发的第一步。正确的环境配置和项目结构可以为后续开发奠定良好的基础。今天我们来详细讲解如何初始化一个Flutter for OpenHarmony数独游戏项目。
创建Flutter项目
flutter create sudoku_game
cd sudoku_game
flutter create命令创建一个新的Flutter项目。项目名称使用小写字母和下划线,这是Dart的命名约定。创建完成后进入项目目录。
项目结构
sudoku_game/
├── lib/
│ ├── main.dart
│ ├── controllers/
│ │ └── game_controller.dart
│ ├── models/
│ │ └── game_move.dart
│ ├── pages/
│ │ ├── main_page.dart
│ │ ├── game/
│ │ ├── stats/
│ │ ├── daily/
│ │ └── settings/
│ └── utils/
│ └── puzzle_generator.dart
├── ohos/
├── pubspec.yaml
└── README.md
lib目录包含所有Dart代码。controllers存放状态管理类,models存放数据模型,pages存放页面组件,utils存放工具类。ohos目录包含OpenHarmony平台相关的配置。
配置pubspec.yaml
name: sudoku_game
description: A Sudoku game built with Flutter for OpenHarmony.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
flutter:
sdk: flutter
get: ^4.6.5
flutter_screenutil: ^5.9.0
convex_bottom_bar: ^3.2.0
table_calendar: ^3.0.9
shared_preferences: ^2.2.2
dependencies列出项目依赖的包。get是状态管理库,flutter_screenutil是屏幕适配库,convex_bottom_bar是底部导航栏库,shared_preferences是本地存储库。
开发依赖配置
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.1
flutter:
uses-material-design: true
dev_dependencies是开发时使用的依赖。flutter_lints提供代码规范检查。uses-material-design启用Material Design图标。
安装依赖
flutter pub get
flutter pub get下载并安装pubspec.yaml中列出的所有依赖。这个命令需要网络连接,首次运行可能需要一些时间。
添加OpenHarmony平台支持
flutter create --platforms ohos .
这个命令为现有项目添加OpenHarmony平台支持。会在项目根目录创建ohos文件夹,包含OpenHarmony平台的配置文件。
OpenHarmony构建配置
{
"app": {
"signingConfigs": [],
"products": [
{
"name": "default",
"signingConfig": "default",
"compatibleSdkVersion": "5.0.0(12)",
"runtimeOS": "OpenHarmony"
}
],
"buildModeSet": [
{ "name": "debug" },
{ "name": "release" }
]
},
build-profile.json5配置OpenHarmony的构建选项。products定义产品配置,compatibleSdkVersion指定兼容的SDK版本。
模块配置
"modules": [
{
"name": "entry",
"srcPath": "./entry",
"targets": [
{
"name": "default",
"applyToProducts": ["default"]
}
]
}
]
}
modules定义模块配置。entry是应用的入口模块,targets指定构建目标。
创建GameController
import 'package:get/get.dart';
import '../models/game_move.dart';
import '../utils/puzzle_generator.dart';
class GameController extends GetxController {
final PuzzleGenerator _generator = PuzzleGenerator();
List<List<int>> board = [];
List<List<int>> solution = [];
List<List<bool>> isFixed = [];
List<List<Set<int>>> notes = [];
GameController是游戏的核心控制器,管理所有游戏状态。继承GetxController获得生命周期方法和更新通知功能。
控制器状态变量
int selectedRow = -1;
int selectedCol = -1;
String difficulty = 'Easy';
int elapsedSeconds = 0;
bool isPaused = false;
bool isComplete = false;
bool notesMode = false;
int hintsUsed = 0;
List<GameMove> moveHistory = [];
void onInit() {
super.onInit();
generateNewGame('Easy');
}
}
定义游戏所需的各种状态变量。onInit在控制器创建后调用,初始化一个简单难度的游戏。
创建数据模型
class GameMove {
final int row;
final int col;
final int? previousValue;
final int? newValue;
final Set<int>? previousNotes;
final Set<int>? newNotes;
final DateTime timestamp;
GameMove({
required this.row,
required this.col,
this.previousValue,
this.newValue,
this.previousNotes,
this.newNotes,
DateTime? timestamp,
}) : timestamp = timestamp ?? DateTime.now();
}
GameMove记录一次游戏操作,用于支持撤销功能。使用final确保数据不可变,使用可空类型处理不同类型的操作。
创建谜题生成器
import 'dart:math';
class PuzzleGenerator {
final Random _random = Random();
List<List<int>> generateSolution() {
List<List<int>> board = List.generate(9, (_) => List.filled(9, 0));
_fillBoard(board);
return board;
}
}
PuzzleGenerator负责生成数独谜题。使用Random类产生随机数,generateSolution生成完整的数独解答。
运行项目
flutter run -d <device_id>
flutter devices
flutter run启动应用,-d参数指定目标设备。flutter devices列出所有连接的设备,包括模拟器和真机。
调试配置
void main() {
if (kDebugMode) {
debugPrint('Running in debug mode');
}
runApp(const MyApp());
}
kDebugMode是Flutter提供的常量,在调试模式下为true。可以用它来区分开发和生产环境,添加调试日志或测试数据。
屏幕适配配置
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 812),
minTextAdapt: true,
splitScreenMode: true,
ScreenUtilInit是flutter_screenutil的初始化组件。designSize设置设计稿的尺寸,这里使用iPhone X的尺寸375x812。
GetMaterialApp配置
builder: (context, child) {
return GetMaterialApp(
title: '数独游戏',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const MainPage(),
);
},
);
}
}
GetMaterialApp是GetX提供的MaterialApp替代品,内置了路由管理、依赖注入等功能。debugShowCheckedModeBanner设为false隐藏调试标签。
配置路由
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 812),
builder: (context, child) {
return GetMaterialApp(
title: '数独游戏',
debugShowCheckedModeBanner: false,
initialRoute: '/',
getPages: [
GetPage(name: '/', page: () => const MainPage()),
GetPage(name: '/game', page: () => const GamePage()),
GetPage(name: '/stats', page: () => const StatsPage()),
GetPage(name: '/daily', page: () => const DailyPage()),
GetPage(name: '/settings', page: () => const SettingsPage()),
],
getPages定义应用的路由表。每个GetPage包含路由名称和对应的页面组件。initialRoute设置初始路由。
主题配置
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
);
},
);
}
}
theme配置应用的主题颜色。ColorScheme.fromSeed从种子颜色生成完整的配色方案。useMaterial3启用Material Design 3。
注册全局控制器
void main() {
WidgetsFlutterBinding.ensureInitialized();
Get.put(GameController());
runApp(const MyApp());
}
WidgetsFlutterBinding.ensureInitialized确保Flutter绑定已初始化。Get.put注册GameController为全局单例,在整个应用中都可以访问。
配置assets资源
flutter:
uses-material-design: true
assets:
- assets/images/
- assets/sounds/
- assets/fonts/
assets配置应用使用的资源文件。images目录存放图片,sounds目录存放音效,fonts目录存放自定义字体。
配置自定义字体
flutter:
fonts:
- family: GameFont
fonts:
- asset: assets/fonts/game_font.ttf
- asset: assets/fonts/game_font_bold.ttf
weight: 700
fonts配置自定义字体。family是字体族名称,在代码中引用。fonts列表包含字体文件,可以指定不同的weight。
使用自定义字体
Text(
'数独游戏',
style: TextStyle(
fontFamily: 'GameFont',
fontSize: 24.sp,
fontWeight: FontWeight.bold,
),
)
fontFamily指定使用的字体族名称,必须与pubspec.yaml中定义的family一致。fontSize使用.sp单位实现字体大小的屏幕适配。
启动页配置
class SplashPage extends StatefulWidget {
const SplashPage({super.key});
State<SplashPage> createState() => _SplashPageState();
}
class _SplashPageState extends State<SplashPage> {
void initState() {
super.initState();
_navigateToHome();
}
Future<void> _navigateToHome() async {
await Future.delayed(const Duration(seconds: 2));
if (mounted) {
Navigator.pushReplacementNamed(context, '/');
}
}
SplashPage是应用的启动页。initState中调用_navigateToHome,延迟2秒后跳转到主页。mounted检查组件是否还在树中。
启动页UI
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.grid_on, size: 80.sp, color: Colors.blue),
SizedBox(height: 24.h),
Text(
'数独游戏',
style: TextStyle(
fontSize: 32.sp,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
],
),
),
);
}
}
启动页显示应用图标和名称。使用.sp和.h单位确保在不同设备上显示一致。pushReplacementNamed替换当前路由,用户无法返回启动页。
OpenHarmony模块配置
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone",
"tablet"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
module.json5配置OpenHarmony应用的模块信息。deviceTypes指定支持的设备类型,包括手机和平板。
能力配置
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:layered_image",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startIcon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
}
]
}
}
abilities定义应用的能力,包括入口、图标、标签等。skills配置应用的启动方式,entity.system.home表示可以从桌面启动。
字符串资源配置
{
"string": [
{
"name": "module_desc",
"value": "数独游戏模块"
},
{
"name": "EntryAbility_desc",
"value": "数独游戏"
},
{
"name": "EntryAbility_label",
"value": "数独"
}
]
}
string.json定义应用使用的字符串资源。使用资源引用而不是硬编码字符串,便于国际化和维护。
颜色资源配置
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}
color.json定义应用使用的颜色资源。start_window_background是启动窗口的背景色。使用资源引用可以方便地统一管理应用的颜色主题。
环境变量配置
class AppConfig {
static const bool isDebug = bool.fromEnvironment('DEBUG', defaultValue: true);
static const String apiBaseUrl = String.fromEnvironment(
'API_BASE_URL',
defaultValue: 'https://api.example.com',
);
static void printConfig() {
if (isDebug) {
debugPrint('Running in debug mode');
debugPrint('API Base URL: $apiBaseUrl');
}
}
}
AppConfig集中管理应用的配置。fromEnvironment从编译时环境变量读取值,可以在构建时通过–dart-define传入。
构建时传入环境变量
flutter build apk --dart-define=DEBUG=false --dart-define=API_BASE_URL=https://prod.api.com
–dart-define参数在构建时传入环境变量。这些值会在编译时替换代码中的fromEnvironment调用。这种方式比运行时配置更安全。
错误处理配置
void main() {
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.presentError(details);
if (kReleaseMode) {
_reportError(details.exception, details.stack);
}
};
FlutterError.onError捕获Flutter框架的错误。在发布模式下上报错误,在调试模式下显示错误详情。
异步错误处理
runZonedGuarded(() {
WidgetsFlutterBinding.ensureInitialized();
Get.put(GameController());
runApp(const MyApp());
}, (error, stackTrace) {
if (kReleaseMode) {
_reportError(error, stackTrace);
} else {
debugPrint('Caught error: $error');
debugPrint('Stack trace: $stackTrace');
}
});
}
void _reportError(dynamic error, StackTrace? stackTrace) {
// 上报错误到服务器或崩溃收集平台
}
runZonedGuarded捕获异步代码中的未处理异常。kReleaseMode判断是否是发布模式,在发布模式下上报错误,在调试模式下打印到控制台。
总结
项目初始化与环境配置的关键步骤:创建项目(使用flutter create命令)、配置依赖(在pubspec.yaml中添加需要的包)、添加平台支持(为OpenHarmony平台生成配置文件)、创建项目结构(组织代码文件)、配置屏幕适配和路由、配置资源文件和应用图标、设置环境变量和错误处理。
完善的项目初始化可以为后续开发打下坚实的基础。合理的配置和清晰的结构让代码更易于维护和扩展。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)