Android Flutter搭建记录

一、搭建Flutter环境

Flutter官网: https://flutterchina.club/

windows环境搭建过程

  1. 由于在国内访问Flutter有时可能会受到限制,为了能正常获取Flutter SDK以及之后第一次执行 flutter doctor 能够正常完成,可以先添加镜像地址,Flutter官方为中国开发者搭建了临时镜像。

    环境变量加入到用户环境变量中:

export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn

或者:

1595225032715
1595225043271

2.下载Flutter SDK

如果下载慢无法下载可以使用git下载:

 git clone -b beta https:*//github.com/flutter/flutter.git

3.将安装包zip解压到你想安装Flutter SDK的路径 ,即为你的Flutter安装目录

4.在Flutter安装目录的flutter文件下找到flutter_console.bat,双击运行并启动flutter命令行

1594956092871

5.打开一个新的命令提示符或PowerShell窗口并运行以下命令以查看是否需要安装任何依赖项来完成安装:

运行 flutter doctor,第一次运行有点慢,需要点等待时间

flutter doctor: 下载它自己的依赖项并自行编译

1594957325718

6.Android Studio安装flutter插件,重启

1594957469700
1594957513096

到此说明flutter环境已经搭建完成

二、新建Flutter工程

如果首次创建项目如果一直卡在Creating Flutter project上,先打开项目文件,如果里面已经有了创建的文件,那么从任务管理器关掉Android Studio,然后重新打开,点击Open an existing Android Studio project,找到项目文件,打开就行了。

1594957559893

工程的目录结构:

1594967329016

官方文档中:

在这个示例中,你将主要编辑Dart代码所在的 lib/main.dart 文件,

我们可以修改main.dart文件来实现简单的信息修改。

我们先运行看一下效果:

1594967413771

三、热重载

看一下main.dart文件

class MyApp extends StatelessWidget {
  // 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.green,
        // This makes the visual density adapt to the platform that you run
        // the app on. For desktop platforms, the controls will be smaller and
        // closer together (more dense) than on mobile platforms.
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

注释中提示我们可以改变

primarySwatch: Colors.green,来修改app的主题颜色,我们来试着修改颜色并体验 热重载

1594967682076

反应很快,日志显示1122ms完成了热重载。

四、使用外部包(package)

我们参考官网提供的步骤,使用 english_words 开源软件包

flutter一些开源的软件包:https://pub.dev/flutter/packages

1.pubspec文件管理Flutter应用程序的assets(资源,如图片、package等) ,所以我们可以到 pubspec.yaml 添加package的依赖

1594968118124
  1. 单击右上角的 Packages get,这会将依赖包安装到您的项目。您可以在控制台中看到以下内容:
1594968242387
  1. lib/main.dart 中, 引入 english_words
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

4.使用词库

1594968486519

此时假如package,热更新会报错,所以需要先停止运行,在运行

之后每一次热更新,MyHomePage的title都会随机出现一个单词。

以上主要是记录软件包package的使用

五、页面跳转

Flutter的路由和导航功能 ,管理多个页面时有两个核心概念和类:RouteNavigator。 一个route是一个屏幕或页面的抽象,Navigator是管理route的Widget。Navigator可以通过route入栈和出栈来实现页面之间的跳转。

1.简单的页面跳转

我们先实现简单的页面跳转,可以用 Navigator.push 和 Navigator.pop 实现

1.新建一个page,新建newpage.dart文件,继承StatelessWidget,可以看到需要复写createState

如果不复写,那么只能抽象,或者noSuchMethod,一会能跳转了我们可以验证一下noSuchMethod是什么意思。

1594969991861

在页面显示一串Text,并定义onPressed动作的事件,使用pop返回到上一页

   @override
  Widget build(BuildContext context) {
    return Scaffold(appBar: AppBar(title: Text('New page'),),
      body: Center(child: RaisedButton(
          child: Text('点击返回上一页'),
          onPressed: () {
            Navigator.pop(context);
          }),),);
  }

2.main.dart找到悬浮按钮的点击事件,删除setState,添加跳转动作

  void _incrementCounter() {
     Navigator.push(context,
        MaterialPageRoute(builder: (context) => Newpage()));
  }

3.热重载,查看并点击悬浮按钮,跳转到了第二个页面,点击可以返回到上一页

1595212566594

2.通过routes路径方式跳转

创建 MaterialApp 时可以指定 routes 参数,该参数是一个映射路由名称和构造器的 Map。

 return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.green,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      routes: {
        "home": (context) => MyHomePage(),
        "newpage": (context) => Newpage()
      },
      home: MyHomePage(title: wordPair.asPascalCase),
    );

跳转直接使用Navigator.pushNamed(context, "newpage");

Navigator.pushNamed(context, "newpage");

3.传递数据到下一个页面

传递的方式有两种:

  • 在构造方法中传递数据
  • 在Route中传递数据给下一个页面

分别来使用一下两种方式

第一种:在跳转的时候将参数传入,在接受页面使用ModalRoute.of(context).settings.arguments获取

class User {
  String name;
  int age;

  String toString(){
    return ("name:" + this.name + " age:" + this.age.toString());
  }
  User({this.name, this.age});
}



void startNewPage() {
//     Navigator.push(context,
//        MaterialPageRoute(builder: (context) => Newpage()));

//     Navigator.pushNamed(context, "newpage");
  
     Navigator.pushNamed(context, "newpage", arguments: User(name: "GodV",age: 23));
     
  }

接受数据, ModalRoute.of(context).settings.arguments;方式获取传递的数据

@override
  Widget build(BuildContext context) {
    //构造获取传递过来的参数user
    final User user = ModalRoute.of(context).settings.arguments;
    return Scaffold(appBar: AppBar(title: Text("第二页"),),
      body: Center(child: RaisedButton(
          child: Text('传递的参数:' + user.toString()),
          onPressed: () {
            Navigator.pop(context);
          }),),);
  }

第二种: onGenerateRoute定义了返回的目标页面需要带有参数,另外目标页面写构造参数

 return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.green,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      routes: {
        "home": (context) => MyHomePage(),
      },
      onGenerateRoute: (settings) {
        if (settings.name == "newpage") {
          final User args = settings.arguments;
          return MaterialPageRoute(builder: (context) {
            return Newpage(
              user: args,
            );
          });
        }
      },
      home: MyHomePage(title: wordPair.asPascalCase),
    );

发送数据的方法没有改变:

 Navigator.pushNamed(context, "newpage", arguments: User(name: "Zgg01",age: 23));

接受页面:

  final User user;
  Newpage({this.user});
  
  @override
  Widget build(BuildContext context) {
    //构造获取传递过来的参数user
    return Scaffold(appBar: AppBar(title: Text("第二页"),),
      body: Center(child: RaisedButton(
          child: Text('传递的参数:' + user.toString()),
          onPressed: () {
            Navigator.pop(context);
          }),),);
  }

4.接收页面返回值

定义接受值显示在Text上

   String reciveString = "接受的值:";
  
  children: <Widget>[
            Text(
              '路由跳转',
            ),
            Text(
              '$reciveString',
//              style: Theme.of(context).textTheme.headline4,
            ),
          ],

打开页面时使用then接受返回回来的参数,需要使用setState来更新组件的值

         Navigator.pushNamed(context, "newpage", arguments: User(name: "Zgg01",age: 23))
              .then((value) => {
                 setState(() {
                   reciveString = value.toString();
                  })
               });

在第二个页面发送需要返回的值

  @override
  Widget build(BuildContext context) {
    //构造获取传递过来的参数user
    return Scaffold(appBar: AppBar(title: Text("第二页"),),
      body: Center(child: RaisedButton(
          child: Text('传递的参数:' + user.toString()),
          onPressed: () {
            Navigator.pop(context, User(name: "Forever",age: 20));
          }),),);
  }

第二个页面点击返回后效果:

1595229311210

六、添加一个 有状态的部件(Stateful widget)

部件 分为状态可变的和状态不可变的

Stateless widgets 是不可变的, 这意味着它们的属性不能改变 - 所有的值都是最终的.

Stateful widgets 持有的状态可能在widget生命周期中发生变化. 实现一个 stateful widget 至少需要两个类:

  1. 一个 StatefulWidget类。
  2. 一个 State类。 StatefulWidget类本身是不变的,但是 State类在widget生命周期中始终存在.

以官网的添加一个ListView为例

第一步:定义一个Page继承StatefulWidget

class ListPage extends StatefulWidget {
  final String title;
  ListPage({Key key, this.title}) : super(key: key);
  @override
  State<StatefulWidget> createState() {
     return ListWordsState();
  }
}

第二步:定义ListView页面,定义ListWordsState继承State<ListPage>

class ListWordsState extends State<ListPage>{
  final _suggestions = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("ListView"),),
      body: new ListView.builder(
          padding: const EdgeInsets.all(16.0),
          // 对于每个建议的单词对都会调用一次itemBuilder,然后将单词对添加到ListTile行中
          // 在偶数行,该函数会为单词对添加一个ListTile row.
          // 在奇数行,该函数会添加一个分割线widget,来分隔相邻的词对。
          // 注意,在小屏幕上,分割线看起来可能比较吃力。
          itemBuilder: (context, i) {
            // 在每一列之前,添加一个1像素高的分隔线widget
            if (i.isOdd) return new Divider();

            // 语法 "i ~/ 2" 表示i除以2,但返回值是整形(向下取整),比如i为:1, 2, 3, 4, 5
            // 时,结果为0, 1, 1, 2, 2, 这可以计算出ListView中减去分隔线后的实际单词对数量
            final index = i ~/ 2;
            // 如果是建议列表中最后一个单词对
            if (index >= _suggestions.length) {
              // ...接着再生成10个单词对,然后添加到建议列表
              _suggestions.addAll(generateWordPairs().take(10));
            }
            return _buildRow(_suggestions[index]);
          }
       ),
    );
  }

  //生成每一个Item的TextView
  Widget _buildRow(WordPair pair) {
    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

}

注意:复写Widget build(BuildContext context)时,需要return一个以Scaffold为根布局的组件,之后再body里面定义ListView

padding:即对应Android的padding,可以使用all修改全部的内边距,或者使用fromLTRB定义每一个方向的内边距


  const EdgeInsets.all(double value)
    : left = value,
      top = value,
      right = value,
      bottom = value;
      
      
const EdgeInsets.fromLTRB(this.left, this.top, this.right, this.bottom);

itemBuilder: (context, i):生成item时的遍历

_buildRow:定义每一个Item的内容

第三步:添加交互

点击收藏,显示收藏图标

1.添加一个 _saved Set(集合) 到RandomWordsState

class ListWordsState extends State<ListPage>{
  final _suggestions = <WordPair>[];
  final _biggerFont = const TextStyle(fontSize: 18.0);
  //添加一个 _saved Set(集合) 到RandomWordsState
  final _saved = new Set<WordPair>();
 }
  1. _buildRow 方法中添加 alreadySaved来检查确保单词对还没有添加到收藏夹中。
  2. 定义trailing: new Icon图标,通过alreadySaved状态来设置颜色
  3. 定义onTap方法,更新对应item的收藏状态
Widget _buildRow(WordPair pair) {
    final alreadySaved = _saved.contains(pair);

    return new ListTile(
      title: new Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
      trailing: new Icon(
        alreadySaved ? Icons.favorite : Icons.favorite_border,
        color: alreadySaved ? Colors.red : null,
      ),
      onTap: () {
        setState(() {
          if (alreadySaved) {
            _saved.remove(pair);
          } else {
            _saved.add(pair);
          }
        });
      },
    );
  }

效果:

1595237316272

关于Flutter和Android对应的Api可以参考: https://flutterchina.club/flutter-for-android/#flutter%E5%92%8Candroid%E4%B8%AD%E7%9A%84view

以上Demo GitHub地址: https://github.com/heezier/Flutter

参考:

Flutter中文官网

Flutter + Tensorflow Lite环境配置

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,158评论 4 370
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,600评论 1 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,785评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,655评论 0 220
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,075评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,002评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,146评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,918评论 0 211
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,671评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,838评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,318评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,636评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,343评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,187评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,982评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,126评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,934评论 2 279