项目初始化是开发的第一步。正确的环境配置和项目结构可以为后续开发奠定良好的基础。今天我们来详细讲解如何初始化一个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

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐