首页 > 其他分享 >Flutter 学习笔记:再次重新学习Flutter

Flutter 学习笔记:再次重新学习Flutter

时间:2024-05-21 13:43:08浏览次数:24  
标签:Widget const https Text 重新学习 笔记 context Flutter

目录

前言

作为一个开发了一年多的Uniapp的.NET 开发工程师,我打算去用FLutter完整的写一个小的程序的Demo。经过两年的编程开发学习,我作为一个普通的程序员,有我的技术选型的思考。

  • 必须主流,不一定是最受欢迎的,但是也是市场占比比较大的。比如前端的React,Vue2,Vue3。我认为你三个选择哪一个都可以,你喜欢就行。跨平台目前的主流技术是Uniapp,React Native和Flutter,我个人选择了Flutter。
  • 生态一定要很完善,Uniapp的生态就很差,虽然背靠Web生态,但是Uniapp的Web和普通的Web还是有区别的。而且Uniapp的社区很差,本身就是兼容国内的小程序生态的。
  • 选择简单易上手的方式,能用组件就用组件,能快速实现就快速实现。保持“不求甚解”的态度,先不用知道底层是怎么写的,代码能跑,需求能满足就行。等你很了解,很熟练了之久,再去研究也不迟。
  • 能真实的用到业务中。移动端开发的需求应该挺常见的,打算Flutter熟练使用之后,后面的开发都用Flutter了

相关链接

Flutter中文文档:https://docs.flutter.cn/get-started/install

构建您的第一个 Flutter 应用:https://codelabs.developers.google.com/codelabs/flutter-codelab-first?hl=zh-cn#8

Flutter 开发学习笔记(0):环境配置:https://blog.csdn.net/qq_44695769/article/details/137133411?spm=1001.2014.3001.5501

Flutter 开发学习笔记(1):第一个简单的Flutter项目(上):https://blog.csdn.net/qq_44695769/article/details/137176032?spm=1001.2014.3001.5501

Flutter 开发学习笔记(2):第一个简单的Flutter项目(下):https://blog.csdn.net/qq_44695769/article/details/137186682?spm=1001.2014.3001.5501

Flutter 开发学习笔记(3):第三方UI库的引入:https://blog.csdn.net/qq_44695769/article/details/137266619?spm=1001.2014.3001.5501

环境安装

因为好久没写Flutter了,打算从新开始再走一遍官方的新手教程

常见问题

Flutter新建项目运行报错Exception in thread “main” java.net.ConnectException: Connection timed out: connect:https://www.cnblogs.com/chorkiu/p/14767567.html

Flutter运行第一个项目时出现javax.net.ssl.SSLHandshakeException的一些解决思路:https://blog.csdn.net/fwhdzh/article/details/106632072

Flutter卡在Running ‘gradle assembleDebug‘最完整解决:https://blog.csdn.net/qq_43596067/article/details/107710915

Flutter编译卡在Running Gradle task ‘assembleDebug‘:https://blog.csdn.net/shiyangkai/article/details/124632441?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171169554516800215013010%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171169554516800215013010&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-124632441-null-null.142%5Ev100%5Econtrol&utm_term=Flutter%20Running%20Gradle%20task%20assembleDebug...&spm=1018.2226.3001.4187

运行新建Flutter项目, 报错Exception in thread “main“ java.net.ConnectException: Connection timed out: connect:https://blog.csdn.net/a18339063397/article/details/125506390?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171169533516800225518971%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=171169533516800225518971&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-125506390-null-null.142%5Ev100%5Econtrol&utm_term=Flutter%20Exception%20in%20thread%20main%20java.net.ConnectException%3A%20Connection%20refused%3A%20connect&spm=1018.2226.3001.4187

Android studio配置Flutter开发环境报错问题解决:https://blog.csdn.net/lu202032/article/details/134421156?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171169787916800215067985%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=171169787916800215067985&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~times_rank-2-134421156-null-null.142%5Ev100%5Econtrol&utm_term=flutter%20jdk17&spm=1018.2226.3001.4187

环境编译成功

Flutter 开发学习笔记(0):环境配置:https://blog.csdn.net/qq_44695769/article/details/137133411?spm=1001.2014.3001.5501

跟着我这个文章走下去,把里面的地址替换成国内的镜像地址,SDK换成本地SDK,编译一遍通过就OK了。

项目启动页面

分析项目

程序入口

Flutter的程序入口是一个 main函数,在默认的示例里面,指向的是一个MyApp。Flutter的程序编写,更像是面向组件化的程序逻辑编写,Flutter为什么称之为嵌套地狱,因为他特别喜欢用组件化的思想

Wiget

在Flutter中,所有的组件都是继承Widget的,就像网页的Div一样,在Flutter中,一切皆widget

在Flutter中写代码,感觉就像写无限细分的Div组件一样

<A>
  </B>
</A>
...
<B>
  </C>
</B>
...
<C>
  </D>
</C>
...
<D>
  </F>
</D>

动态更新

在Widget中,分为无状态Widget和有状态Widget。简单来说,动态Widget中,会给你一个修改变量的钩子,有点像React,让你去修改Widget的中显示的参数,其实就是一个setState的钩子,这个和React或者Vue3的函数式编程的感觉很像

深度理解Flutter:有状态Widget与无状态Widget的详细对比:https://blog.csdn.net/HaiJun_Aion/article/details/135341129

如果你写过WPF,就知道WPF里面有一个依赖属性。在Flutter里面的状态和这个依赖属性差不多。状态就是Flutter的共享内存空间,无状态不代表Widget不可更改,而是Widget不维护内存空间,可以通过有状态的父组件更新广播,让无状态的子组件更新。

比如我们修改一下默认的启动项目

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

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
        useMaterial3: true,
      ),
      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: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            // Text(
            //   '${_counter}',
            //   style: Theme.of(context).textTheme.headlineMedium,
            // ),
            TextWidget(text: '无状态Widget [${_counter}]')
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

class TextWidget extends StatelessWidget {
  const TextWidget({Key? key, required this.text}) : super(key: key);
  final String text;

  @override
  Widget build(BuildContext context) {
    return Text(
      text,
      textAlign: TextAlign.center,
      style: const TextStyle(
        fontSize: 40,
        color: Colors.red,
      ),
    );
  }
}

如果不清楚该怎么选择,那就能用无状态Widget,就用无状态Widget

按照教程初始化项目

构建您的第一个 Flutter 应用,创建项目:https://codelabs.developers.google.com/codelabs/flutter-codelab-first?hl=zh-cn#2

相关的配置我就不展开了,大家进去看看好了。

import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();

    return Scaffold(
      body: Column(
        children: [
          Text('A random idea:'),
          Text(appState.current.asLowerCase),
        ],
      ),
    );
  }
}

弱化Flutter编译检查

analysis_options.yaml中进行替换

include: package:flutter_lints/flutter.yaml

linter:
  rules:
    prefer_const_constructors: false
    prefer_final_fields: false
    use_key_in_widget_constructors: false
    prefer_const_literals_to_create_immutables: false
    prefer_const_constructors_in_immutables: false
    avoid_print: false

添加第一个按钮

注意,我这里的代码可能和教程的有点区别,有部分更新

    return Scaffold(
      body: Column(
        children: [
          const Text('A random idea:'),
          Text(appState.current.asLowerCase),
          ElevatedButton(
              onPressed: () {
                debugPrint('Hello Flutter!');
              },
              child: const Text("Click me"))
        ],
      ),
    );

快速嵌套组件化

由于我之前已经写过一次新手教程了,我后面就挑我觉得重要的讲了

有时候快捷键会卡住,得切换成英文输入法才行

数据更新:ChangeNotifier和StatefulWidget

用C# 的思想,ChangeNotifier其实就是全局变量。全局变量是成为屎山的重要前提,能不用全局变量就不用全局变量。数据流的流通应该是组件和组件之间互相引用的。反正就是能用StatefulWidget就不要用ChangeNotifier。

Dart中的委托

C# 最重要的三大设计,委托,Task和Linq。委托更是重中之重。委托其实就是指针的封装。

Dart中的委托,和C# 中的不太一样

final Function getNext;//Dart
Action getNext;//C#
final bool Function(string msg) isFavoriate;//Dart
Function<bool,string> isFavoriate;//C#

修改好的代码

import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:provider/provider.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => null,
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

void showMsg(String msg) {
  debugPrint("[main]: ${msg}");
}

class FavoriateModel {
  var current = WordPair.random();

  ///喜欢列表
  var favorites = <WordPair>[];
}

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

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

class _MyHomePageState extends State<MyHomePage> {
  var _selectedIndex = 0;
  FavoriateModel favorate = new FavoriateModel();

  void getNext() {
    setState(() {
      favorate.current = WordPair.random();
    });
  }

  /// 添加喜欢
  void toggleFavorite() {
    setState(() {
      if (favorate.favorites.contains(favorate.current)) {
        favorate.favorites.remove(favorate.current);
        showMsg("remove => ${favorate.current}");
      } else {
        favorate.favorites.add(favorate.current);
        showMsg("add => ${favorate.current}");
      }
    });
  }

  bool isFavoriate (WordPair str) {
    return this.favorate.favorites.contains(str);
  }

  @override
  Widget build(BuildContext context) {
    Widget _page;
    switch (_selectedIndex) {
      case 0:
        _page = GeneratorPage(
          appState: this.favorate,
          toggleFavorite: this.toggleFavorite,
          getNext: this.getNext,
          isFavoriate: this.isFavoriate,
        );
        break;
      case 1:
        _page = FavoraitePage(
          appState: favorate,
        );
        break;
      default:
        throw UnimplementedError("no widget in ${_selectedIndex}");
    }

    return LayoutBuilder(builder: (context, constraints) {
      return Scaffold(
        body: Row(
          children: [
            SafeArea(
              child: NavigationRail(
                extended: constraints.maxWidth >= 600,
                destinations: const [
                  NavigationRailDestination(
                    icon: Icon(Icons.home),
                    label: Text('Home'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite),
                    label: Text('Favorites'),
                  ),
                ],
                selectedIndex: _selectedIndex,
                onDestinationSelected: (value) {
                  showMsg('selected: $value');
                  setState(() {
                    _selectedIndex = value;
                  });
                },
              ),
            ),
            Expanded(
              child: Container(
                color: Theme.of(context).colorScheme.primaryContainer,
                child: _page,
              ),
            ),
          ],
        ),
      );
    });
  }
}

class FavoraitePage extends StatelessWidget {
  const FavoraitePage({super.key, required this.appState});

  final FavoriateModel appState;

  @override
  Widget build(BuildContext context) {
    var _message = <String>[];
    appState.favorites.forEach((item) {
      _message.add(item.toString());
    });
    _message.insert(0, "message");
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: _message.map((e) => Text(e)).toList(),
      ),
    );
  }
}

class GeneratorPage extends StatelessWidget {
  const GeneratorPage(
      {super.key,
      required this.appState,
      required this.toggleFavorite,
      required this.getNext,
      required this.isFavoriate});

  final Function toggleFavorite;
  final Function getNext;
  final bool Function(WordPair msg) isFavoriate;
  final FavoriateModel appState;

  @override
  Widget build(BuildContext context) {
    var pair = appState.current;

    IconData icon;

    if (this.isFavoriate(pair)) {
      icon = Icons.favorite;
    } else {
      icon = Icons.favorite_border;
    }

    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          BigCard(pair: pair),
          const SizedBox(height: 10),
          Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              ElevatedButton.icon(
                onPressed: () {
                  toggleFavorite();
                },
                icon: Icon(icon),
                label: const Text('Like'),
              ),
              const SizedBox(width: 10),
              ElevatedButton(
                onPressed: () {
                  getNext();
                },
                child: const Text('Next'),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class BigCard extends StatelessWidget {
  const BigCard({
    super.key,
    required this.pair,
  });

  final WordPair pair;

  @override
  Widget build(BuildContext context) {
    final _theme = Theme.of(context); //获取容器的样式

    return Center(
      child: Card(
        color: _theme.colorScheme.primary,
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Text(pair.asLowerCase),
        ),
      ),
    );
  }
}

运行效果

总结

还是不习惯使用Dart,语法上来说还可以,就是编辑器比较恶心。在Anriod Studio 里面,缩进是不能改的,默认2格。而且代码提示很垃圾,不说像Visual studio里面一样的智能提示了,连'switch','required'这种关键词有时候都提示不出来,怪不得Visual studio 用的人多。但是我又懒得配置VS Code,毕竟VS code 配置起来也比较的麻烦。至少不影响运行,还能接受。

标签:Widget,const,https,Text,重新学习,笔记,context,Flutter
From: https://www.cnblogs.com/gclove2000/p/18201696

相关文章

  • tensorflow.js示例笔记 - mnist
    使用层来进行数字识别,使用tf.layersapi训练模型识别MNIST数据库中的手写数字。index.html<html><head><title>MNIST</title><metacharset="UTF-8"><metaname="viewport"content="width=device-width,......
  • tensorflow.js示例笔记 - boston-housing
    多元回归,比较不同的房价预测模型。index.html<htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1"><title>Mult......
  • tensorflow.js示例笔记 - iris
    根据鸢尾花的数据对花进行分类,使用神经网络对结构化(表格)数据进行分类。index.html<html><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1"><linkrel="......
  • FRP笔记
    后台启动nohup./frps-cfrps.ini>/dev/null2>&1&系统服务启动frpssudosystemctlstartfrps再打开自启动sudosystemctlenablefrps如果要重启应用,可以这样,sudosystemctlrestartfrps如果要停止应用,可以输入,sudosystemctlstopfrps如果要查看应用的日志,可以输......
  • 软考笔记二
    软考笔记二目录软考笔记二浮点数的加减法运算一、对阶二、尾数求和三、规格化四、舍入五、溢出判断浮点数的加减法运算前言:运算过程:对阶、尾数求和、规格化、舍入、溢出判断在计算机中,加减法运算用补码实现。算术运算的常识:两个浮点数如果要进行加减法运算,它们的阶或者指......
  • C++基础知识学习笔记(1)
    资料来源https://www.bilibili.com/video/BV1et411b73Z/?spm_id_from=333.337.search-card.all.click&vd_source=cc561849591f6a210152150b2493f6f3简单知识点创建项目用VS创建了一个C++的空项目。在【源文件】中创建一个cpp文件书写以下代码并运行#include<iostr......
  • 【论文笔记】跨语言知识图谱补全
    2012~20181.Co-trainingembeddingsofknowledgegraphsandentitydescriptionsforcross-lingualentityalignment文章核心观点:参考上一篇关于实体对齐的分析方法:单语预测(KDCoE-mono)旨在以TransE的相同方式查询三元组(h,r,t)中缺失的h或t跨语预测(KDCoE-cross)提供了一种新......
  • 学习笔记
    CPP:函数指针定义方法int(func)(inti);int(p)(int)=func......
  • mit6.828笔记 - lab4 Part C:抢占式多任务和进程间通信(IPC)
    PartC:抢占式多任务和进程间通信(IPClab4到目前为止,我们能够启动多个CPU,让多个CPU同时处理多个进程。实现了中断处理,并且实现了用户级页面故障机制以及写时复制fork。但是,我们的进程调度不是抢占式的,现在每个进程只有在发生中断的时候,才会被调度(调用shed_yeild),这样就有可能会有......
  • manacher学习笔记
    小学一下。首先是用一个在回文串题目中的的技巧,用来减少分讨,如果想到这个的话说不定thusc2024d1t1就切了。具体来说,就是在每个字符之间都插入一个#,然后在开头和结尾插入随便两个不同的字符。然后就只有回文中心在字符上的情况了。首先设\(p_{i}\)为当前位置为中心的最大回文半......