Flutter速来系列2: 永远要跟这个世界说声Hello,计数器

251 阅读4分钟

是的,我们新建一个Flutter程序。

本来想跟这个世界说声Hello,Flutter想告诉人们

需要好好赚钱,所以给你了你一个计数器。

一、所有Flutter人都知道的计数器

Android studio 新建一个Flutter项目。

你会看到如下代码

如果运行不起来报错,很大可能是gradle版本问题,新建一个普通的安卓项目,看一下gradle的信息

classpath 'com.android.tools.build:gradle:7.1.1'


distributionUrl=https://services.gradle.org/distributions/gradle-7.2-all.zip

找到这两个gradle配置的版本号,对应修改一下Flutter的信息。

原本


import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  // This widget is the home page of your application. It is stateful, meaning
  // that it has a State object (defined below) that contains fields that affect
  // how it looks.

  // This class is the configuration for the state. It holds the values (in this
  // case the title) provided by the parent (in this case the App widget) and
  // used by the build method of the State. Fields in a Widget subclass are
  // always marked "final".

  final String title;

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

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

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // This method is rerun every time setState is called, for instance as done
    // by the _incrementCounter method above.
    //
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        // Here we take the value from the MyHomePage object that was created by
        // the App.build method, and use it to set our appbar title.
        title: Text(widget.title),
      ),
      body: Center(
        // Center is a layout widget. It takes a single child and positions it
        // in the middle of the parent.
        child: Column(
          // Column is also a layout widget. It takes a list of children and
          // arranges them vertically. By default, it sizes itself to fit its
          // children horizontally, and tries to be as tall as its parent.
          //
          // Invoke "debug painting" (press "p" in the console, choose the
          // "Toggle Debug Paint" action from the Flutter Inspector in Android
          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
          // to see the wireframe for each widget.
          //
          // Column has various properties to control how it sizes itself and
          // how it positions its children. Here we use mainAxisAlignment to
          // center the children vertically; the main axis here is the vertical
          // axis because Columns are vertical (the cross axis would be
          // horizontal).
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}


中文版

emm,英文下,好像有点不太亲切,那么就改成中文吧

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // 这个小部件是你应用程序的根部件。
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter演示',
      theme: ThemeData(
        // 这是你应用程序的主题。
        //
        // 尝试用 "flutter run" 运行你的应用程序。
        // 你会看到应用程序有一个蓝色的工具栏。
        // 然后,在不退出应用程序的情况下,尝试将 primarySwatch 更改为 Colors.green,
        // 然后触发 "热重载"(在你运行 "flutter run" 的控制台中按下 "r"),
        // 或者直接保存更改以进行 "热重载"(在 Flutter IDE 中)。
        // 注意,计数器没有重置为零;应用程序并没有重新启动。
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter演示主页'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  // 这个小部件是你应用程序的主页。它是有状态的,意味着它有一个包含影响外观的字段的 State 对象(在下面定义)。

  // 这个类是状态的配置。它保存了父级(在本例中是 App 小部件)提供的值(在本例中是标题),
  // 并且由 State 的 build 方法使用。小部件子类中的字段始终标记为 "final"。

  final String title;

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

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

  void _incrementCounter() {
    setState(() {
      // 这个对 setState 的调用告诉 Flutter 框架状态发生了变化,
      // 这导致重新运行下面的 build 方法,以便显示可以反映更新后的值的内容。
      // 如果我们在不调用 setState() 的情况下更改 _counter,
      // 那么 build 方法将不会再次被调用,因此似乎什么也没有发生。
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    // 每次调用 setState(例如由 _incrementCounter 方法调用)时,都会重新运行这个方法。
    //
    // Flutter 框架经过优化,使得重新运行 build 方法非常快速,
    // 所以你只需要重建需要更新的内容,而不必逐个更改小部件的实例。
    return Scaffold(
      appBar: AppBar(
        // 这里我们从由 App.build 方法创建的 MyHomePage 对象中取得值,并将其用作我们的 appbar 标题。
        title: Text(widget.title),
      ),
      body: Center(
        // Center 是一个布局小部件。它接受一个单独的子部件并将其放置在父部件的中间位置。
        child: Column(
          // Column 也是一个布局小部件。它接受一个子部件列表并垂直地排列它们。
          // 默认情况下,它会水平地调整自身大小以适应其子部件,
          // 并尝试尽量与其父部件一样高。
          //
          // 调用 "debug painting"(在控制台中按下 "p",在 Android Studio 中选择 Flutter Inspector 中的 "Toggle Debug Paint" 操作,
          // 或在 Visual Studio Code 中选择 "Toggle Debug Paint" 命令),
          // 可以看到每个小部件的线框。
          //
          // Column 有各种属性来控制其自身的大小和其子部件的位置。
          // 在这里,我们使用 mainAxisAlignment 来垂直居中子部件;
          // 这里的主轴是垂直轴,因为 Column 是垂直的(交叉轴将是水平的)。
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              '你已经按下按钮的次数:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: '增加',
        child: const Icon(Icons.add),
      ), // 这个尾逗号使得构建方法的自动格式更加友好。
    );
  }
}


但是
但是
但是
但是
但是
但是

emm,看起来是舒服一些,但是又感觉太长了

简单版

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // 这个小部件是你应用程序的根部件。
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter演示',
      theme: ThemeData(
        // 这是你应用程序的主题。
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter演示主页'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  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(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              '你已经按下按钮的次数:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: '增加',
        child: const Icon(Icons.add),
      ), // 这个尾逗号使得构建方法的自动格式更加友好。
    );
  }
}

嗯,往简单了说,main是入口,启动了app,MyHomePage就是页面。

哇,简单,哇完事,哇,完结撒花。


二、两两三三概念事,最简单也最困难

程序根入口是main

  • Flutter 应用中 main 函数为应用程序的入口。main 函数中调用了runApp 方法,它的功能是启动Flutter应用。runApp它接受一个 Widget参数,在本示例中它是一个MyApp对象,MyApp()是 Flutter 应用的根组件。

全世界都是widget

  • MyApp类代表 Flutter 应用,它继承了 StatelessWidget类,这也就意味着应用本身也是一个widget。
  • 在 Flutter 中,大多数东西都是 widget(后同“组件”或“部件”),包括对齐(Align)、填充(Padding)、手势处理(GestureDetector)等,它们都是以 widget 的形式提供。
  • Flutter 在构建页面时,会调用组件的build方法,widget 的主要工作是提供一个 build() 方法来描述如何构建 UI 界面(通常是通过组合、拼装其他基础 widget )。
  • MaterialApp 是Material 库中提供的 Flutter APP 框架,通过它可以设置应用的名称、主题、语言、首页及路由列表等。MaterialApp也是一个 widget。

参考: book.flutterchina.club/chapter2/fi…