Day05 - Flutter开发初体验

简介: Day05 - Flutter开发初体验

一、创建Flutter项目


创建Flutter项目有两种方式:通过命令行创建 和 通过开发工具创建

  • 1.1、在终端通过命令行创建(全新的APP)


flutter create 项目的名字


提示:

  • Flutter的名称不要包含特殊的字符,另外不可以使用驼峰标识(不要使用大写),名字很长可以使用下划线连接
  • 创建完之后使用自己喜欢的开发工具打开即可,比如:Android SdudioVSCode

创建扩展: 语言的设置

  • flutter create -i swift -a kotlin 项目的名字
  • flutter create -i swift -a java 项目的名字
  • flutter create -i objc -a kottlin 项目的名字
  • flutter create -i objc -a java 项目的名字

2>、混合项目的创建(混编到已有的Android / iOS工程内)

  • flutter create --template module 组件的名字

3>、Flutter Plugin的创建(Flutter平台插件工程,包含Dart层与Native平台层的实现)

  • flutter create --template=plugin xxapp_plugin

4>、Flutter Package(Flutter纯Dart插件工程,仅包含Dart层的实现,往往定义一些公共Widget)

  • flutter create --template=package xxapp_package


  • 1.2、目录结构概述

image.pngimage.png


  • dart_tool:记录一些东西所在的位置以及它的一些版本信息,不需要我们去配置,不要手动去修改
  • idea: 记录当前项目的一些配置的
  • iml:也是对记录对当前项目的配置
  • Android:安卓的代码
  • build:项目的构建输出目录
  • iOS:iOS的代码
  • lib:flutter 源码的文件夹
  • test:主要是用来做一些测试的
  • gitignore:记录当前的一些忽略的
  • metadate:对flutter版本的一些记录,不需要手动去改
  • packages、pubspec.lock 都是导入到三方后生成的
  • pubspec.yaml:项目依赖配置文件类似于RN的 package.json


  • 1.3、Flutter Hot Reload热重载 和  Flutter Hot Restart  热重启 的区别


image.png


  • Flutter Hot Reload 热重载:主要是执行 build 方法
  • Flutter Hot Restart  热重启:重新运行整个app(热启动)

提示:冷启动是从未启动过app


二、Flutter代码展示



看不懂没关系,这篇博客仅仅是展示,下篇才开始学习

  • 2.1、Hello World的实现
    我们在 main.dart 里面进行从 0 到 1 编写代码


import 'package:flutter/material.dart';
main(List<String> args) {
     runApp(Text("Hello World", textDirection: TextDirection.ltr));
}
  • 当然,上面的代码我们已经实现了在界面上显示Hello World:
    但是没有居中,字体也有点小;
    这些问题,我们放到后面再来解决,先搞懂目前的几行代码;
    上面的代码我们有一些比较熟悉,有一些并不清楚是什么:
    比如我们知道Dart程序的入口都是main函数,而Flutter是Dart编写的,所以入口也是main函数;
    但是我们导入的Material是什么呢?
    另外,我们在main函数中调用了一个runApp()函数又是什么呢?
    下面,我们对不认识的代码进行一些分析。
  • 2.2、代码分析
  • 1>、runApp 和 WidgetrunApp是Flutter内部提供的一个函数,当我们启动一个Flutter应用程序时就是从调用这个函数开始的
  • 我们可以点到runApp的源码,查看到该函数
  • 我们暂时不分析具体的源码(因为我发现过多的理论,对于初学者来说并不友好)


void runApp(Widget app) {
    ...省略代码
}
  • 该函数让我们传入一个东西:Widget?我们先说Widget的翻译:
  • Widget在国内有很多的翻译;
  • 做过Android、iOS等开发的人群,喜欢将它翻译成控件;
  • 做过Vue、React等开发的人群,喜欢将它翻译成组件;
  • 如果我们使用Google,Widget翻译过来应该是小部件;
  • 没有说哪种翻译一定是对的,或者一定是错的,但是我个人更倾向于小部件或者组件;
  • Widget到底什么东西呢?
  • 我们学习Flutter,从一开始就可以有一个基本的认识:Flutter中万物皆Widget(万物皆可盘);
  • 在我们iOS或者Android开发中,我们的界面有很多种类的划分:应用(Application)、视图控制器(View Controller)、活动(Activity)、View(视图)、Button(按钮)等等;
  • 但是在Flutter中,这些东西都是不同的Widget而已;
  • 也就是我们整个应用程序中所看到的内容几乎都是Widget,甚至是内边距的设置,我们也需要使用一个叫Padding的Widget来做;
  • runApp函数让我们传入的就是一个Widget:
  • 但是我们现在没有Widget,怎么办呢?
  • 我们可以导入Flutter默认已经给我们提供的Material库,来使用其中的很多内置Widget;
  • 2>、Material 设计风格
  • material是什么呢?
  • material是Google公司推行的一套设计风格,或者叫设计语言、设计规范等;
  • 里面有非常多的设计规范,比如颜色、文字的排版、响应动画与过度、填充等等;
  • 在Flutter中高度集成了Material风格的Widget;
  • 在我们的应用中,我们可以直接使用这些Widget来创建我们的应用(后面会用到很多);
  • Text小部件分析:
  • 我们可以使用Text小部件来完成文字的显示;
  • 我们发现Text小部件继承自StatelessWidget,StatelessWidget继承自Widget;
  • 所以我们可以将Text小部件传入到runApp函数中
  • 属性非常多,但是我们已经学习了Dart语法,所以你会发现只有this.data属性是必须传入的。


class Text extends StatelessWidget {
    const Text(
    this.data, {
    Key key,
    this.style,
    this.strutStyle,
    this.textAlign,
    this.textDirection,
    this.locale,
    this.softWrap,
    this.overflow,
    this.textScaleFactor,
    this.maxLines,
    this.semanticsLabel,
    this.textWidthBasis,
    });
}
  • StatelessWidget简单介绍:
  • StatelessWidget继承自Widget;
  • 后面我会更加详细的介绍它的用法;


abstract class StatelessWidget extends Widget {
   // ...省略代码
}
  • 2.3、代码改进
  • 1>、改进界面样式
    我们可能希望文字居中显示,并且可以大一些;
    居中显示: 需要使用另外一个Widget,Center;
    文字大一些: 需要给Text文本设置一些样式;
    我们修改代码如下:
    我们在Text小部件外层包装了一个Center部件,让Text作为其child;
    并且,我们给Text组件设置了一个属性:style,对应的值是TextStyle类型;


import 'package:flutter/material.dart';
main(List<String> args) {
    runApp(Center(
        child: Text(
           "Hello World",
           textDirection: TextDirection.ltr,
           style: TextStyle(fontSize: 20),
        ),
     )
   );
}
  • 2>、改进界面结构目前我们虽然可以显示HelloWorld,但是我们发现最底部的背景是黑色,并且我们的页面并不够结构化。正常的App页面应该有一定的结构,比如通常都会有导航栏,会有一些背景颜色等在开发当中,我们并不需要从零去搭建这种结构化的界面,我们可以使用Material库,直接使用其中的一些封装好的组件来完成一些结构的搭建。我们通过下面的代码来实现:


import 'package:flutter/material.dart';
main(List<String> args) {
    runApp(
       MaterialApp(
          home: Scaffold(
             appBar: AppBar(
                 title: Text("CODERWHY"),
             ),
             body: Center(
                 child: Text(
                 "Hello World",
                 textDirection: TextDirection.ltr,
                 style: TextStyle(fontSize: 36),
             ),
          ),
       ),
    )
  );
}
  • 在最外层包裹一个MaterialApp
  • 这意味着整个应用我们都会采用MaterialApp风格的一些东西,方便我们对应用的设计,并且目前我们使用了其中两个属性;
  • title:这个是定义在Android系统中打开多任务切换窗口时显示的标题;(暂时可以不写)
  • home:是该应用启动时显示的页面,我们传入了一个Scaffold;
  • Scaffold是什么呢?
  • 翻译过来是脚手架,脚手架的作用就是搭建页面的基本结构;
  • 所以我们给MaterialApp的home属性传入了一个Scaffold对象,作为启动显示的Widget;
  • Scaffold也有一些属性,比如appBar和body;
  • appBar是用于设计导航栏的,我们传入了一个title属性;
  • body是页面的内容部分,我们传入了之前已经创建好的Center中包裹的一个Text的Widget;
  • 2.4、代码重构
  • 1>、创建自己的Widget很多学习Flutter的人,都会被Flutter的嵌套劝退,当代码嵌套过多时,结构很容易看不清晰。
  • 这里有两点我先说明一下:
  • 1、Flutter整个开发过程中就是形成一个Widget树,所以形成嵌套是很正常的。
  • 2、关于Flutter的代码缩进,更多开发中我们使用的是2个空格(前端开发2个空格居多,你喜欢4个也没问题)
  • 但是,我们开发一个这么简单的程序就出现如此多的嵌套,如果应用程序更复杂呢?
  • 我们可以对我们的代码进行封装,将它们封装到自己的Widget中,创建自己的Widget;
  • 如何创建自己的Widget呢?
  • 在Flutter开发中,我们可以继承自StatelessWidget或者StatefulWidget来创建自己的Widget类;
  • StatelessWidget: 没有状态改变的Widget,通常这种Widget仅仅是做一些展示工作而已;
  • StatefulWidget: 需要保存状态,并且可能出现状态改变的Widget;
  • 在上面的案例中对代码的重构,我们使用StatelessWidget即可,所以我们接下来学习一下如果利用StatelessWidget来对我们的代码进行重构;
    StatefulWidget我们放到后面的一个案例中来学习;
  • 2>、StatelessWidget
  • StatelessWidget通常是一些没有状态(State,也可以理解成data)需要维护的Widget:
  • 它们的数据通常是直接写死(放在Widget中的数据,必须被定义为final,为什么呢?我在下一个章节讲解StatefulWidget会讲到);
  • 从parent widget中传入的而且一旦传入就不可以修改;
  • 从InheritedWidget获取来使用的数据(这个放到后面会讲解);
  • 我们来看一下创建一个StatelessWidget的格式:
  • 让自己创建的Widget继承自StatelessWidget;
  • StatelessWidget包含一个必须重写的方法:build方法;


class MyStatelessWidget extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
       return <返回我们的Widget要渲染的Widget,比如一个Text Widget>;
   }
}
  • build方法的解析:
  • Flutter在拿到我们自己创建的StatelessWidget时,就会执行它的build方法;
  • 我们需要在build方法中告诉Flutter,我们的Widget希望渲染什么元素,比如一个Text Widget;
  • StatelessWidget没办法主动去执行build方法,当我们使用的数据发生改变时,build方法会被重新执行;
  • build方法什么情况下被执行呢 ?
  • 当我们的StatelessWidget第一次被插入到Widget树中时(也就是第一次被创建时);
  • 当我们的父Widget(parent widget)发生改变时,子Widget会被重新构建;
  • 如果我们的Widget依赖InheritedWidget的一些数据,InheritedWidget数据发生改变时;
  • 3>、重构案例代码



image.png

image.png

  • 现在我们就可以通过StatelessWidget来对我们的代码进行重构了
  • 因为我们的整个代码都是一些数据展示,没有数据的改变,使用StatelessWidget即可;
  • 另外,为了体现更好的封装性,我对代码进行了两层的拆分,让代码结构看起来更加清晰;(具体的拆分方式,我会在后面的案例中不断的体现出来,目前我们先拆分两层)
  • 重构后的代码如下:


import 'package:flutter/material.dart';
main(List<String> args) {
    runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
      return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
            title: Text("CODERWHY"),
          ),
          body: HomeContent(),
      ),
    )
  }
}
class HomeContent extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      return Center(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
                Checkbox(
                   value: true,
                   onChanged: (value) => print("Hello World")
             ),
             Text(
                "同意协议",
                textDirection: TextDirection.ltr,
                style: TextStyle(fontSize: 20),
             )
           ],
         ),
      );
   }
}


  • 2.5、StateFulWidget (有状态的)在开发中,某些Widget情况下我们展示的数据并不是一层不变的:比如Flutter默认程序中的计数器案例,点击了+号按钮后,显示的数字需要+1;比如在开发中,我们会进行下拉刷新、上拉加载更多,这时数据也会发生变化;而StatelessWidget通常用来展示哪些数据固定不变的,如果数据会发生改变,我们使用StatefulWidget;
  • 1>、StatefulWidget介绍为什么选择StatefulWidget呢?在示例代码中,当我们点击按钮时,界面上显示的数据会发生改变;这时,我们需要一个变量来记录当前的状态,再把这个变量显示到某个Text Widget上;并且每次变量发生改变时,我们对应的Text上显示的内容也要发生改变;但是有一个问题,我之前说过定义到Widget中的数据都是不可变的,必须定义为final,为什么呢?这次因为Flutter在设计的时候就决定了一旦Widget中展示的数据发生变化,就重新构建整个Widget;Flutter通过一些机制来限定定义到Widget中的成员变量必须是final的;
  • Flutter如何做到我们在开发中定义到Widget中的数据一定是final的呢?,我们来看一下Widget的源码


@immutable
abstract class Widget extends DiagnosticableTree {
     // ...省略代码
}


  • @immutable 注解标明的类或者子类都必须是不可变的

提示:定义到Widget中的数据一定是不可变的,需要使用final来修饰

  • 2>、如何存储Widget状态?
  • 既然Widget是不可变,那么StatefulWidget如何来存储可变的状态呢?
  • StatelessWidget无所谓,因为它里面的数据通常是直接定义完后就不修改的。
  • 但StatefulWidget需要有状态(可以理解成变量)的改变,这如何做到呢?
  • Flutter将StatefulWidget设计成了两个类:
  • 也就是你创建StatefulWidget时必须创建两个类:
  • 一个类继承自StatefulWidget,作为Widget树的一部分;
  • 一个类继承自State,用于记录StatefulWidget会变化的状态,并且根据状态的变化,构建出新的Widget;
  • 创建一个StatefulWidget,我们通常会按照如下格式来做:
  • 当Flutter在构建Widget Tree时,会获取State的实例,并且它调用build方法去获取StatefulWidget希望构建的Widget;
  • 那么,我们就可以将需要保存的状态保存在MyState中,因为它是可变的;


class MyStatefulWidget extends StatefulWidget {
     @override
     State<StatefulWidget> createState() {
         // 将创建的State返回
         return MyState();
     }
}
class MyState extends State<MyStatefulWidget> {
     @override
     Widget build(BuildContext context) {
         return <构建自己的Widget>;
     }
}

提示:Flutter这样设计的目的是:在Flutter中,只要数据改变了Widget就需要重新构建(rebuild)


  • 2.6、StatefulWidget案例我们通过一个案例来练习一下StatefulWidget,还是之前的计数器案例,但是我们按照自己的方式进行一些改进。案例效果以及布局如下:
  • 在这个案例中,有很多布局对于我们来说有些复杂,我们后面会详细学习,建议大家根据我的代码一步步写出来来熟悉Flutter开发模式;
  • Column小部件:之前我们已经用过,当有垂直方向布局时,我们就使用它;
  • Row小部件:之前也用过,当时水平方向布局时,我们就使用它;
  • RaiseButton小部件:可以创建一个按钮,并且其中有一个onPress属性是传入一个回调函数,当按钮点击时被回调;

image.png

image.png


  • 1>、 创建StatefulWidget,下面我们来看看代码实现:
  • 为当点击按钮时,数字会发生变化,所以我们需要使用一个StatefulWidget,所以我们需要创建两个类;
  • MyCounterWidget继承自StatefulWidget,里面需要实现createState方法;
  • MyCounterState继承自State,里面实现build方法,并且可以定义一些成员变量


class MyCounterWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
       // 将创建的State返回
       return MyCounterState();
  }
}
class MyCounterState extends State<MyCounterWidget> {
    int counter = 0;
   @override
   Widget build(BuildContext context) {
       return Center(
           child: Text("当前计数:$counter", style: TextStyle(fontSize: 30),),
       );
   }
}
  • 2>、实现按钮的布局


class MyCounterState extends State<MyCounterWidget> {
    int counter = 0;
    @override
    Widget build(BuildContext context) {
       return Center(
           child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                        RaisedButton(
                           color: Colors.redAccent,
                           child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
                           onPressed: () {
                           },
                        ),
                        RaisedButton(
                           color: Colors.orangeAccent,
                           child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
                           onPressed: () {
                           },
                        )
                    ],
                  ),
                 Text("当前计数:$counter", style: TextStyle(fontSize: 30),)
                ],
           ),
       );
   }
}


  • 3>、按钮点击状态改变
  • 我们现在要监听状态的改变,当状态改变时要修改counter变量:
  • 但是,直接修改变量可以改变界面吗?不可以。
  • 这是因为Flutter并不知道我们的数据发生了改变,需要来重新构建我们界面中的Widget;
  • 如何可以让Flutter知道我们的状态发生改变了,重新构建我们的Widget呢?
  • 我们需要调用一个State中默认给我们提供的setState方法;
  • 可以在其中的回调函数中修改我们的变量;


onPressed: () {
   setState(() {  
      counter++;  
   });   
},
  • 2.7、StatefulWidget 生命周期
  • 1>、生命周期的理解
  • 什么是生命周期呢?
  • 客户端开发:iOS开发中我们需要知道UIViewController从创建到销毁的整个过程,Android开发中我们需要知道Activity从创建到销毁的整个过程。以便在不同的生命周期方法中完成不同的操作;
  • 前端开发中:Vue、React开发中组件也都有自己的生命周期,在不同的生命周期中我们可以做不同的操作;
  • Flutter小部件的生命周期:
  • StatelessWidget可以由父Widget直接传入值,调用build方法来构建,整个过程非常简单;
  • 而StatefulWidget需要通过State来管理其数据,并且还要监控状态的改变决定是否重新build整个Widget;
  • 所以,我们主要讨论StatefulWidget的生命周期,也就是它从创建到销毁的整个过程;
  • 2>、生命周期那么StatefulWidget有哪些生命周期的回调呢?它们分别在什么情况下执行呢?atefulWidget本身由两个类组成的:StatefulWidget和State,我们分开进行分析首先,执行StatefulWidget中相关的方法:
  • 1、执行StatefulWidget的构造函数(Constructor)来创建出StatefulWidget;
  • 2、执行StatefulWidget的createState方法,来创建一个维护StatefulWidget的State对象;
  • 其次,调用createState创建State对象时,执行State类的相关方法:
  • 1、执行State类的构造方法(Constructor)来创建State对象;
  • 2、执行initState,我们通常会在这个方法中执行一些数据初始化的操作,或者也可能会发送网络请求;
  • 3、执行didChangeDependencies方法,这个方法在两种情况下会调用
  • 情况一:调用initState会调用;
  • 情况二:从其他对象中依赖一些数据发生改变时,比如前面我们提到的InheritedWidget;
  • 4、Flutter执行build方法,来看一下我们当前的Widget需要渲染哪些Widget;
  • 5、当前的Widget不再使用时,会调用dispose进行销毁;
  • 6、手动调用setState方法,会根据最新的状态(数据)来重新调用build方法,构建对应的Widgets;
  • 7、执行didUpdateWidget方法是在当父Widget触发重建(rebuild)时,系统会调用didUpdateWidget方法;


  • 过代码进行演示:


import 'package:flutter/material.dart';
main(List<String> args) {
    runApp(MyApp());
}
class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
         return MaterialApp(
             home: Scaffold(
               appBar: AppBar(
                   title: Text("HelloWorld"),
               ),
             body: HomeBody(),
          ),
     );
  }
}
class HomeBody extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
      print("HomeBody build");
      return MyCounterWidget();
   }
}
class MyCounterWidget extends StatefulWidget {
   MyCounterWidget() {
       print("执行了MyCounterWidget的构造方法");
   }
   @override
   State<StatefulWidget> createState() {
        print("执行了MyCounterWidget的createState方法");
        // 将创建的State返回
        return MyCounterState();
    }
}
class MyCounterState extends State<MyCounterWidget> {
     int counter = 0;
     MyCounterState() {
        print("执行MyCounterState的构造方法");
     }
     @override
     void initState() {
         super.initState();
         print("执行MyCounterState的init方法");
     }
     @override
     void didChangeDependencies() {
        // TODO: implement didChangeDependencies
        super.didChangeDependencies();
        print("执行MyCounterState的didChangeDependencies方法");
     }
     @override
     Widget build(BuildContext context) {
         print("执行执行MyCounterState的build方法");
         return Center(
               child: Column(
                 mainAxisAlignment: MainAxisAlignment.center,
                 children: <Widget>[
                      Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: <Widget>[
                          RaisedButton(
                             color: Colors.redAccent,
                             child: Text("+1", style: TextStyle(fontSize: 18, color: Colors.white),),
                            onPressed: () {
                               setState(() {
                                   counter++;
                               });
                            },
                          ),
                         RaisedButton(
                             color: Colors.orangeAccent,
                             child: Text("-1", style: TextStyle(fontSize: 18, color: Colors.white),),
                             onPressed: () {
                                 setState(() {
                                    counter--;
                                 });
                             },
                         )
                      ],
                   ),
                  Text("当前计数:$counter", style: TextStyle(fontSize: 30),)
              ],
          ),
      );
    }
   @override
   void didUpdateWidget(MyCounterWidget oldWidget) {
          super.didUpdateWidget(oldWidget);
           print("执行MyCounterState的didUpdateWidget方法");
   }
   @override
   void dispose() {
      super.dispose();
      print("执行MyCounterState的dispose方法");
   }
}
  • 打印结果如下:


flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行了MyCounterWidget的createState方法
flutter: 执行MyCounterState的构造方法
flutter: 执行MyCounterState的init方法
flutter: 执行MyCounterState的didChangeDependencies方法
flutter: 执行执行MyCounterState的build方法
// 注意:Flutter会build所有的组件两次(查了GitHub、Stack Overflow,目前没查到原因)
flutter: HomeBody build
flutter: 执行了MyCounterWidget的构造方法
flutter: 执行MyCounterState的didUpdateWidget方法
flutter: 执行执行MyCounterState的build方法
  • 当我们改变状态,手动执行setState方法后会打印如下结果:


flutter: 执行执行MyCounterState的build方法


目录
相关文章
Doodle Jump — 使用Flutter&Flame开发游戏真不错!
用Flutter&Flame开发游戏是一种什么体验?最近网上冲浪的时候,我偶然发现了一个国外的游戏网站,类似于国内的4399。在浏览时,我遇到了一款经典的小游戏:Doodle Jump...
112767 12
|
3月前
|
安全 Go 数据安全/隐私保护
Flutter开发笔记:Flutter路由技术
Flutter开发笔记:Flutter路由技术
343 0
|
15天前
|
移动开发 前端开发 JavaScript
移动端 Hybrid 开发:RN、Flutter与Webview的抉择与融合
【4月更文挑战第6天】本文对比了移动端Hybrid开发的三种主流方案——React Native (RN),Flutter和Webview。RN基于JavaScript,适合React熟练的团队,适用于性能要求高、跨平台的中大型应用。Flutter,使用Dart语言,以其高性能和自定义UI适用于追求极致体验的项目。Webview适合快速移植Web应用至移动端,开发成本低但性能受限。选择时要考虑项目规模、性能需求、团队技术栈等因素,实际应用中常采用混合策略,如RN/Flutter+Webview、原生模块集成等,以实现最佳开发效果和长期技术规划。
53 0
|
3月前
|
开发者 索引 容器
Flutter开发笔记:Flutter 布局相关组件
Flutter开发笔记:Flutter 布局相关组件
121 0
|
3月前
|
开发框架 Dart 开发工具
从零基础到精通:Flutter开发的完整指南
从零基础到精通:Flutter开发的完整指南
118 0
|
4月前
|
存储 网络安全 数据库
flutter怎样使用阿里云开发服务?
flutter怎样使用阿里云开发服务?
102 2
|
4月前
|
Dart 监控 开发者
跨平台应用的选择:Flutter下电脑局域网控制软件开发
近年来,跨平台应用的需求不断增加,开发人员纷纷寻找适用于多种操作系统的解决方案。本文将探讨在Flutter框架下开发电脑局域网控制软件的过程,并提供一些实用的代码示例。
234 1
|
4月前
|
存储 网络安全 数据库
flutter怎样使用阿里云开发服务?
flutter怎样使用阿里云开发服务?
57 0
|
4月前
|
API 开发工具 Android开发
flutter怎样使用阿里云开发服务?
flutter怎样使用阿里云开发服务?
44 1