admin 发布的文章

1引言

在 Flutter 应用开发中,状态管理是一个核心挑战。随着应用复杂度的提升,如何高效地管理状态、处理依赖注入以及实现路由导航,成为了开发者必须面对的问题。本文将深入探讨 GetX 框架的特点、基础使用以及高级应用场景,帮助开发者全面掌握这一框架。

2 框架特点

GetX集成了状态管理、依赖注入和路由管理三大核心功能。与其他状态管理方案相比,GetX的最大优势在于其简洁的语法和极低的学习曲线,同时保持了高性能和强大的功能。使用 GetX,开发者可以用更少的代码实现更复杂的功能,大大提高开发效率,很适合在下面的几个场景中使用:

  1. 中小型应用开发;
  2. 需要快速迭代的项目;

2.1 状态管理

getx采用了响应式的编程的思想Obx避免不必要的UI刷新,只有真正需要更新的widget的才会被重建,从而提高应用的性能,它比其他传统的状态管理方案占用的内存更小,响应速度更快;

import 'package:flutter/material.dart';
import 'package:get/get.dart';
// 创建一个控制器类,继承自GetxController
class CounterController extends GetxController{
  // 创建一个响应式变量,初始值为0// 通过obs后缀,我们将普通变量转换为可观察的响应式变量var count = 0.obs;
  // 增加计数的方法void increment() {
    count.value++;
  }
}
void main() {
  // 注册控制器,使其在应用中全局可用Get.put(CounterController());
  runApp(MyApp());
}
class MyApp extends StatelessWidget{
  @overrideWidget build(BuildContext context) {
    return GetMaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('GetX Counter')),
        body: Center(
          // 使用Obx包裹需要监听状态变化的widget,// 当里面的响应式变量发生变化时,Obx 会自动更新对应widget的内容child: Obx(() => Text(
                'Count: ${Get.find<CounterController>().count}',
                style: TextStyle(fontSize: 24),
              )),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () => Get.find<CounterController>().increment(),
          child: Icon(Icons.add),
        ),
      ),
    );
  }
}

本系列文章共有三篇,计划分别从Flutter、Dart、工具三个方面介绍,快速形成对Flutter开发的一个综合概念。

本文主要介绍Flutter相关知识。

Flutter语言使用 Dart,开发IDE使用VSCode、Android Studio 或 IntelliJ IDEA,个人推荐使用VSCode。

Flutter概览

Flutter是由Google开发的开源移动应用开发框架,用于快速构建高性能、高保真度的跨平台移动应用。Flutter支持同一套代码在 iOS、Android、Web、桌面应用等多个平台上运行,使用自带的 Skia 图形引擎,可以直接绘制 UI 组件,这使得 Flutter 应用在性能方面表现出色,滚动流畅,响应迅速(目前正在使用 Impeller 替换 Skia)。

看一眼有个印象即可。


参考资料

三棵树

Flutter的渲染机制由三棵树组成:Widget Tree、Element Tree、Render Tree(渲染树)。

所有Widget组成Widget Tree。

通过调用Widget的createElement()方法,创建Element Tree,Wdiget Tree与Element Tree是一一对应的。

每个Element调用 createRenderObject()形成Render Tree,Render Tree负责渲染,注意Render Tree与Elemeng Tree并不是一一对应的,而是为最后渲染做准备。

StatelessWidget

// StatelessWidget 定义
abstract class StatelessWidget extends Widget {
  const StatelessWidget({ super.key });

  @override
  StatelessElement createElement() => StatelessElement(this);
  
  // 核心是 build 方法
  @protected
  Widget build(BuildContext context);
}

// 示例,Echo widget,核心是build方法构建UI
class Echo extends StatelessWidget  {
  const Echo({
    Key? key,  
    required this.text,
    this.backgroundColor = Colors.grey, //默认为灰色
  }):super(key:key);
    
  final String text;
  final Color backgroundColor;

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: backgroundColor,
        child: Text(text),
      ),
    );
  }
}



// 在子树中获取父级widget的一个示例:
class ContextRoute extends StatelessWidget  {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Context测试"),
      ),
      body: Container(
        child: Builder(builder: (context) {
          // 在 widget 树中向上查找最近的父级`Scaffold`  widget
          Scaffold scaffold = context.findAncestorWidgetOfExactType<Scaffold>();
          // 直接返回 AppBar的title, 此处实际上是Text("Context测试")
          return (scaffold.appBar as AppBar).title;
        }),
      ),
    );
  }
}


StatefulWidget

StatefulWidget的核心就是有状态,可以修改重绘UI。


StatefulWidget重写了createElement方法,返回的是StatefulElement,StatefulElement可能会多次调用createState()来创建对象。

createState(),创建状态,修改UI必须通过修改状态来实现,一个StatefulElement对应一个State实例。

abstract class StatefulWidget extends Widget {
  const StatefulWidget({ Key key }) : super(key: key);
    
  @override
  StatefulElement createElement() => StatefulElement(this);
    
  @protected
  State createState();
}


添加插件

tutorial_coach_mark: 1.2.11

import 'package:flutter/material.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) => MaterialApp(home: const HomePage());
}

class HomePage extends StatefulWidget{
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>{
  late TutorialCoachMark tutorial;
  final _keyAdd = GlobalKey(),
      _keySearch = GlobalKey(),
      _keyProfile = GlobalKey();

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback((_) => _showTutorial());
  }

  void _showTutorial() {
    tutorial = TutorialCoachMark(
      targets: _createTargets(),
      colorShadow: Colors.black54,
      opacityShadow: 0.7,
      paddingFocus: 8,
      textSkip: '跳过',
      onFinish: () => print('完成'),
      onSkip: () {
        print('跳过');
        return true;
      },
    )..show(context: context);
  }

  List<TargetFocus> _createTargets() => [
        TargetFocus(
          identify: 'add',
          keyTarget: _keyAdd,
          contents: [
            TargetContent(
              align: ContentAlign.bottom,
              builder: (_, __) =>
                  const Text('点击这里添加任务', style: TextStyle(color: Colors.white)),
            )
          ],
        ),
        TargetFocus(
          identify: 'search',
          keyTarget: _keySearch,
          contents: [
            TargetContent(
              align: ContentAlign.top,
              builder: (_, __) =>
                  const Text('快速搜索内容', style: TextStyle(color: Colors.white)),
            )
          ],
        ),
        TargetFocus(
          identify: 'profile',
          keyTarget: _keyProfile,
          contents: [
            TargetContent(
              align: ContentAlign.left,
              builder: (_, __) =>
                  const Text('进入个人中心', style: TextStyle(color: Colors.white)),
            )
          ],
        ),
      ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('局部高亮教程')),
      floatingActionButton: FloatingActionButton(
        key: _keyProfile,
        onPressed: () => _showTutorial(),
        child: const Icon(Icons.person),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              key: _keyAdd,
              onPressed: () {},
              child: const Text('添加'),
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              key: _keySearch,
              onPressed: () {},
              child: const Text('搜索'),
            ),
            const SizedBox(height: 40),
            OutlinedButton(
              onPressed: _showTutorial,
              child: const Text('再看一次'),
            ),
          ],
        ),
      ),
    );
  }
}


<?php
// 检查是否有表单提交
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // 获取用户输入的名字
    $name = htmlspecialchars($_POST['name']);
} else {
    $name = "";
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Simple PHP Page</title>
</head>
<body>
    <h1>Welcome to My PHP Page</h1>
    <p>Current Time: <?php echo date('Y-m-d H:i:s'); ?></p>

    <form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
        <label for="name">Enter your name:</label>
        <input type="text" id="name" name="name" value="<?php echo $name; ?>">
        <input type="submit" value="Submit">
    </form>

    <?php
    if (!empty($name)) {
        echo "<p>Welcome, $name!</p>";
    }
    ?>
</body>
</html>

哈哈哈哈哈