理解第一个Flutter项目

上节课,我们创建了第一个Flutter的项目,项目很简单,里面就一个按钮,点击按钮数字递增。如下是这个项目的目录结构

项目目录

你会看到有很多文件夹和文件,有android web windows lib test 等等的这些文件,那么你要编写的源码是在哪里呢?android? windows?…其实不是,是在lib,打开lib,你会看到有一个main.dart文件

这个lib,以后就是存放你的源码,你要编写的东西是dart,不是java 也不是cpp ,flutter是使用dart语言的。那么为什么目录下面会有 android web windows这些目录的

其实很好理解,flutter之所以能够跨平台编译成不同的应用,它就是靠这些。

Flutter 是一套 Dart 代码、多平台运行框架,lib 文件夹是你写的跨平台通用业务代码; 而 android / windows / web / macos / linux 这些文件夹,是各平台原生宿主工程,负责把 Flutter 嵌入对应系统运行。

1 android 文件夹

它是,安卓平台原生工程

提供安卓 App 运行容器,承载 Flutter 渲染引擎,一些android有关的底层配置,需要在这里配置。例如,Android app的权限配置,图标,包名等等。也就是这里面,你可以配置Android 特有的东西,调用安卓原生 API(相机、蓝牙、通知 位置等等)

2 windows 文件夹

它是Windows 桌面端原生工程

C++ Win32 原生项目,创建 Windows 系统窗口,初始化 Skia 渲染引擎,skia是谷歌的图形化库,flutter的桌面应用是使用它来渲染成窗体和各种控件的。

windows目录里面的东西,就是负责Windows 桌面软件的一些底层的配置,例如窗口大小,窗口的标题栏,图标,系统托盘(windows 右下角那个图标)

你最终可以打包成exe,msix,发布到微软应用商店等等。

3. web 文件夹

Web 静态网页工程(HTML/JS/CSS)提供网页入口 index.html,你的dart源码。最终编译成网页有关的东西。

配置网页标题、图标、静态资源、路由, 编译输出 HTML/CSS/JS,直接部署到服务器在浏览器打开

其他的

由于我们这里没有使用,因为之前我们没有勾选 ios macos Linux 等等,所以这里就没有这些文件夹了。不过原理都一样的。

ios:苹果 iOS 原生 Xcode 工程,打包 ipa 上架 iPhone

macos:macOS 桌面 Cocoa 工程,Mac 电脑客户端

linux:Linux 桌面 GTK 工程

我们在Android studio 的右上角 选择不同的平台,就可以编译不同的版本的应用了。当然你也可以之间使用命令行,其实 本质上,你用android 选择平台 点击那个绿色三角形,本质上也是执行flutter的命令

# 运行安卓,读取 android 目录配置
flutter run -d android

# 运行Windows桌面,读取 windows 目录C++工程
flutter run -d windows

# 运行网页,读取 web 目录html
flutter run -d chromeCode language: PHP (php)

打开windows 下面的 runner / main.cpp

#include <flutter/dart_project.h>
#include <flutter/flutter_view_controller.h>
#include <windows.h>

#include "flutter_window.h"
#include "utils.h"

int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
                      _In_ wchar_t *command_line, _In_ int show_command) {
  // Attach to console when present (e.g., 'flutter run') or create a
  // new console when running with a debugger.
  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
    CreateAndAttachConsole();
  }

  // Initialize COM, so that it is available for use in the library and/or
  // plugins.
  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);

  flutter::DartProject project(L"data");

  std::vector<std::string> command_line_arguments =
      GetCommandLineArguments();

  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));

  FlutterWindow window(project);
  Win32Window::Point origin(10, 10);
  Win32Window::Size size(1280, 720);
  if (!window.Create(L"myproject", origin, size)) {
    return EXIT_FAILURE;
  }
  window.SetQuitOnClose(true);

  ::MSG msg;
  while (::GetMessage(&msg, nullptr, 0, 0)) {
    ::TranslateMessage(&msg);
    ::DispatchMessage(&msg);
  }

  ::CoUninitialize();
  return EXIT_SUCCESS;
}
Code language: PHP (php)

如果你使用windows desktop 编译,那么这个main.cpp 才是真正的入口,先执行这个main.cpp 然后才到你的lib/main.dart

同样地,如果你打开Android 的目录,你会看到里面有一份,和你开发Kotlin 或者java的android app的项目结构基本差不多。

同样有,src目录,AndroidManifest.xml文件 、MainActivity这些都有,是不是和你开发Android 很熟悉。其实没错,本质上你使用flutter 开发android 本质上也是先从这个android目录开始的。

同样,你打开web目录,你同样可以看到 web的入口 index.html

dart文件

在lib 下面的main.dart 就是你以后开发最多的地方了,你可以在lib里面创建你的目录,可以添加其他的dart文件。

打开main.dart

// 导入Material组件库
import 'package:flutter/material.dart';

// 程序入口函数
void main() {
  runApp(const MyApp());
}

// 应用根组件(无状态)
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      // 全局主题配置
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
      ),
      // 首页页面
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

// 首页页面(有状态组件)
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  // 页面标题参数
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

// 首页状态管理类
class _MyHomePageState extends State<MyHomePage> {
  // 计数器变量
  int _counter = 0;

  // 计数器自增方法
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // 页面基础布局脚手架
    return Scaffold(
      // 顶部导航栏
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      // 页面主体内容
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('You have pushed the button this many times:'),
            Text(
              '$_counter', //文字显示上面的数字变量
              style: Theme.of(context).textTheme.headlineMedium, //文字样式
            ),
          ],
        ),
      ),
      // 右下角悬浮按钮 onPressed 点击就执行_incrementCounter toottip是提示文字 child是设置图标
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}Code language: JavaScript (javascript)

1. 入口部分

import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
Code language: JavaScript (javascript)
  • 导入谷歌官方 Material ,它是一套 UI 组件(按钮、页面、文字等),苹果也有一套自己的UI框架
  • main 是 Dart 程序唯一入口;runApp() 启动 App,挂载根组件 MyApp

2. MyApp 全局根组件

它是,StatelessWidget 无状态,后面我们会讲到,暂且知道一下就可以

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // 你应用程序的根组件
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: .fromSeed(seedColor: Colors.deepPurple),
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
Code language: JavaScript (javascript)
  • 全局配置:App 名称、全局主题、首页页面
  • MaterialApp:Material 风格应用外壳,统一管理路由、主题、标题
  • home 指定 App 打开默认页面 MyHomePage

3. MyHomePage 首页页面

打开窗体或者APP默认显示的页面,它是StatefulWidget 有状态,之后我们后面会讲到。

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}Code language: JavaScript (javascript)
  • 带可变数据(计数器数字),所以用有状态组件
  • 外部传入标题 title,通过 widget.title 在状态类里读取
  • 必须配套一个 _MyHomePageState 类,专门存放页面变量、交互逻辑

4. _MyHomePageState

状态逻辑类,用来处理页面逻辑和显示,更新等有关

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(

        backgroundColor: Theme.of(context).colorScheme.inversePrimary,

        title: Text(widget.title),
      ),
      body: Center(

        child: Column(

          mainAxisAlignment: .center,
          children: [
            const Text('You have pushed the button this many times:'),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}Code language: JavaScript (javascript)
  1. int _counter = 0:页面可变数据 —— 计数值
  2. _incrementCounter():按钮点击事件
    • setState() 通知 Flutter:数据变更,重新刷新页面 UI
    • 计数器 + 1
  3. build():每次刷新都会执行,返回页面布局

5. Scaffold 页面脚手架

页面布局模板,属于_MyHomePageState 里面的

  • appBar:顶部标题栏
  • body:页面主体内容
    • Center 居中,Column 垂直排列文字
  • floatingActionButton:右下角圆形加号按钮,绑定点击自增方法

整体运行流程

  1. 程序启动 → 执行main → 加载MyApp
  2. MyApp渲染MaterialApp → 加载首页MyHomePage
  3. 页面渲染出标题栏、居中文字、加号按钮
  4. 点击按钮 → _incrementCounter执行 + setState触发页面重绘 → 数字刷新

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注