Flutter:WahtApp_Clones使用Camera&TabBar&ListView

wahtApp.gif

新建項目、新建包,再新建一個dart文件僵蛛,不同于Java文件會自動填充類目红柱,這是一個空白文件,其次dart中文件名不需要和類型相同宁仔。因為同一個文件中可以存在多個一級類稠屠,而Java多以匿名內(nèi)部類的形式存在。類的創(chuàng)建方式就不做贅述了台诗,需要注意的是筆者用到的時候需要自己手動導(dǎo)包完箩,當(dāng)然如果你記得完整類名也可以用快捷鍵導(dǎo)包,該類是此App的主界面拉队,通常來說直接繼承自`StatelessWidget或者StatefulWidget弊知,F(xiàn)lutter中所有都是Weight(組件),包括padding粱快、color等秩彤。而在Flutter中Weight大致可以分為三類叔扼,也有認(rèn)為分兩類的都可以吧。

  • 第一類:StatelessWidget以及繼承自該類的其他Widget如Text漫雷、ButtonBar等瓜富。此類為無狀態(tài)組件,必須實現(xiàn)其構(gòu)造方法,不需要維護(hù)其狀態(tài)降盹,內(nèi)容通常無法動態(tài)更改与柑。
  • 第二類:StatefulWidget以及繼承自該類的其他Widget如Image、Scaffold等蓄坏。此類為有狀態(tài)的組件价捧,必須實現(xiàn)createState()方法,以創(chuàng)建需要維護(hù)的State涡戳,可以用來更改控件的內(nèi)容或者狀態(tài)结蟋。
  • 第三類:RenderObjectWidget以及直接或者間接繼承自該類的Widget如Padding、Align(對齊)等渔彰。改類多位一些小部件嵌屎,用來修飾其他Widget。

還有就是上面涉及到的導(dǎo)包操作,格式如下

//import '路徑'
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';

這里用到的Widget為StatefulWidget恍涂,導(dǎo)入上述文件中任何一個都可以宝惰,根據(jù)不同平臺進(jìn)行選擇。這三個文件包含內(nèi)容的大小從上到下依次減少乳丰,前兩個文件包含了第三個文件掌测。第一個主要為Material Design風(fēng)格UI等(Android平臺主流風(fēng)格),第二個主要包含Cupertino風(fēng)格UI等(IOS平臺主流風(fēng)格)产园,第三個即常用Widget等汞斧。

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

//規(guī)范:同Java,駝峰命名不推薦下劃線等特殊符號
class WhatsappHome extends StatefulWidget {

    @override
    State createState() {
        return null;
    }
}

接下來為createState()方法創(chuàng)建一個返回值什燕,這種情況可以考慮直接new一個State粘勒,但是會報錯因為State是一個抽象類,所以這里我們自定義一個類屎即,繼承State庙睡。代碼如下

class _WhatsAppHomeState extends State{
  @override
  Widget build(BuildContext context) {
    return null;
  }
}

上面代碼依舊在同一個文件中,必須實現(xiàn)build方法技俐,同時該方法要求返回一個Widget乘陪,前面提到的任何Widget都可以在這里作為返回值,但這里我們選擇使用Scaffold作為返回值雕擂,Scaffold是一個組合Widget啡邑,由Flutter內(nèi)部幫我們將多個Widget組合到一起,實現(xiàn)了基本的Material Design布局結(jié)構(gòu)(Android),通過閱讀源碼可以查看該Widget由那些Widget組合而來井赌,請自行查閱谤逼,Scaffold是及其常用的贵扰,其子Widget中又包含了其他組合Widget比如AppBar等,了解各個Widget有助于以后自定義(組合)布局結(jié)構(gòu)流部。

接下來往Scaffold中填充元素戚绕,根據(jù)上面的效果圖可以分析出來當(dāng)前頁面需要哪些Widget,填充后完整代碼如下

 @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("WhatApp"),
        elevation: 0.7,//陰影
        bottom: new TabBar(//注意這里bottom是指Appbar的bottom
          //硬性規(guī)定:TabBar必須配合TabController一起使用(或者TabController的實現(xiàn)類)枝冀,用于控制tab切換舞丛,否則程序報錯
          controller: new TabController(length: 4, vsync: this),
          indicatorColor: Colors.white,
          //選中時顏色
          tabs: <Widget>[
            new Tab(icon: new Icon(Icons.camera_alt)),
            new Tab(text: "CHATS"),
            new Tab(text: "STATUS"),
            new Tab(text: "CALLS")
          ],
        ),
        //右側(cè)按鈕
        actions: <Widget>[
          new Icon(Icons.search),
          new Padding(padding: const EdgeInsets.symmetric(horizontal: 5.0)),
          //常量建議加上const關(guān)鍵字(非強制),EdgeInsets表示邊緣插入果漾,symmetric為對稱插入方向的描述之一
          new Icon(Icons.more_vert)
        ],
      ),
    );
  }
}

但是如果使用hot load功能程序會出現(xiàn)如下錯誤后來發(fā)現(xiàn)圖片丟了......

看到關(guān)鍵點即可瓷马,可以看到錯誤中提到了一個叫SingleTickerProviderStateMixin的類,并指明了_WhatsAppHomeState需要使用它跨晴,代碼中有注釋明確表示需要配合TickerProvider來使用Tabbar,而錯誤中提到的類也包含這個單詞片林,到這里大致就可以推斷出來SingleTickerProviderStateMixinTickerProvider的一個子類端盆。那替換成它要求的類即可。

在動手前先看下SingleTickerProviderStateMixin的具體實現(xiàn)费封,這里算是難點了
點擊controller屬性進(jìn)入源碼中查看焕妙,可以看到源碼中要求使用TabController,而這里直接new一個對象的話會出現(xiàn)如下錯誤

The argument type 'SingleTickerProviderStateMixin<StatefulWidget>' can't be assigned to the parameter type 'TabController'

大意為SingleTickerProviderStateMixin不能轉(zhuǎn)換為TabController弓摘,點開SingleTickerProviderStateMixin源碼如下

@optionalTypeArgs
mixin SingleTickerProviderStateMixin<T extends StatefulWidget> on State<T> implements TickerProvider {
  Ticker _ticker;
    ...

多的就不看了焚鹊,第一行就夠,很明顯該類實現(xiàn)了TickerProvider韧献,在Dart語法中可以使用implements實現(xiàn)其他類不一定是抽象類末患,如下

abstract class A{

}
abstract class B{

}
class D{

}
abstract class C extends A implements B,D{

}

因為這里是實現(xiàn)關(guān)系,并非繼承所以對象不能轉(zhuǎn)換锤窑。其他該類的聲明過程中還出現(xiàn)了幾個關(guān)鍵詞mixin璧针、on,通常和前兩個關(guān)鍵詞配合使用的還有一個with渊啰,受限于篇幅這里就不詳述了探橱,看下如下代碼應(yīng)該就差不多明白了,說明在注釋中
class A {
void a() {
print("A");
}
}

class B {
  void b() {
    print("B");
  }
}

class D {
  void b() {
    print("D");
  }
}

mixin E on D {}//該行表示類E允許被with多繼承绘证,但是受限于類D隧膏,再直白點的說法就是:再直白點的說法就是:某X使用with多繼承E,則X必須是繼承D

//with用于實現(xiàn)多繼承嚷那,若有同名方法優(yōu)先級從右往左依次從高到低
class C extends A with B, D, E {
  void text() {
    this.a();
    this.b(); //可以調(diào)用到BD中方法胞枕,且輸出D,因為D中b()覆蓋了B中b().
  }
}
//以下下寫法將出錯
//class F with E{}因為F和D并無繼承關(guān)系

這里需要著重理解一下從事移動開發(fā)的對多繼承的概念可能比較薄弱,其次Dart低(高)版本中該關(guān)鍵字使用方法可能有差異车酣。

確保上面的三個關(guān)鍵字理解了曲稼,再回頭看SingleTickerProviderStateMixin得使用就比較明了了索绪。完整代碼如下

import 'package:flutter/material.dart';
import 'package:flutter/src/foundation/diagnostics.dart';
import 'package:flutter_teaching/whatsapp/pages/call_screen.dart';
import 'package:flutter_teaching/whatsapp/pages/camera_screen.dart';
import 'package:flutter_teaching/whatsapp/pages/chat_screen.dart';
import 'package:flutter_teaching/whatsapp/pages/status_screen.dart';

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

//規(guī)范:同Java,駝峰命名不推薦下劃線等特殊符號
class WhatsappHome extends StatefulWidget {
  @override
  State createState() {
    return new _WhatsAppHomeState();
  }
}

class _WhatsAppHomeState extends State with SingleTickerProviderStateMixin {
  TabController _tabController;

  @override
  void initState() {
    super.initState();
    _tabController = new TabController(length: 4, vsync: this, initialIndex: 1);
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("WhatApp"),
        elevation: 0.7, //陰影
        bottom: new TabBar(
          controller: _tabController,
          //硬性規(guī)定:TabBar必須配合TabController一起使用
          indicatorColor: Colors.white,
          //選中時顏色
          tabs: <Widget>[
            new Tab(icon: new Icon(Icons.camera_alt)),
            new Tab(text: "CHATS"),
            new Tab(text: "STATUS"),
            new Tab(text: "CALLS")
          ],
        ),
        actions: <Widget>[
          new Icon(Icons.search),
          new Padding(padding: const EdgeInsets.symmetric(horizontal: 5.0)),
          //常量建議加上const關(guān)鍵字(非強制)贫悄,EdgeInsets表示邊緣插入瑞驱,symmetric為對稱插入方向的描述之一
          new Icon(Icons.more_vert)
        ],
      ),
      //TabBarView表示為TabBar的頁面,該空間會自動按順序關(guān)聯(lián)TabBar,這里簡單創(chuàng)建了4個類
      // 每個類寫法完全相同如下窄坦,記得導(dǎo)入文件
      //class CallsScreen extends StatelessWidget {
      //  @override
      //  Widget build(BuildContext context) {
      //    return new Center(
      //      child: new Text(
      //        "Calls",
      //        style: new TextStyle(fontSize: 20.0),
      //      ),
      //    );
      //  }
      //}
      //注意這是Scaffold中屬性
      body: new TabBarView(
        controller: _tabController,
        children: <Widget>[
          new CameraScreen(),
          new ChatScreen(),
          new StatusScreen(),
          new CallsScreen(),
        ],
      ),
      floatingActionButton: new FloatingActionButton(
          backgroundColor: Theme.of(context).accentColor,
          child: new Icon(
            Icons.message,
            color: Colors.white,
          ),
          onPressed: () => print("open chat")),
    );
  }
}

這里說明一下Dart的語法是嵌套+構(gòu)造的寫法唤反,如果遇到不清楚的Widget不知道有些啥屬性那么點進(jìn)去看一下它的構(gòu)造方法就明白了。這里代碼可能看起來不清晰鸭津,但在AS中會自動填充標(biāo)記所嵌套的層次彤侍。
到目前為止整個功能剩余對攝像頭的支持了,目前效果圖如下

Gif圖片

然而并沒有圖

接下來對camera_screen.dart文件改造下逆趋。Flutter中攝像頭使用的官方鏈接盏阶,大致看一下知道大概哪些步驟。
前面為了方便對四個View都是使用的StatelessWidget闻书,也就是說類容固定名斟,而要使用攝像頭必須使用StatefulWidget,還需要添加依賴魄眉。其次這里還涉及到異步砰盐,文檔第二步中的使用到的await,async,Future就是Flutter中異步操作需要用到的關(guān)鍵詞,常用于網(wǎng)絡(luò)請求坑律,以及耗時任務(wù)等阻塞線程的操作中岩梳。之后初始化CameraController這玩意就是Android中的CameraManager,不初始化無法使用拍照等功能晃择。最后創(chuàng)建一個Widget用于顯示攝像頭獲取到的畫面冀值,這里指定使用CameraPreview來展示畫面。剩余的兩部分別是拍照和展示照片藕各。
第一步:按照文檔中的第一步池摧,添加依賴。添加后pubspec.yaml文件如下激况,添加之后點擊左上角的Packages get同步完成后進(jìn)行下一步

# 注意這里的依賴縮進(jìn)需要注意 縮進(jìn)錯誤會導(dǎo)致找不到包報錯
dependencies:
  flutter:
    sdk: flutter
  camera:
  path_provider:
  path:

第二步:獲取可用Camera列表作彤,并從列表中獲取第一個可用的攝像頭代碼如下

// Obtain a list of the available cameras on the device.
final cameras = await availableCameras();

// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first; 

重點來了,前面講到了mixin乌逐、with竭讳、on三個關(guān)鍵字以及組合使用,這里依舊是三個相互關(guān)聯(lián)的關(guān)鍵字分別是await浙踢、Future绢慢、async

await用在調(diào)用的異步方法時作為前置修飾。若某方法體內(nèi)使用await關(guān)鍵字,則該方法必定需要使用async修飾胰舆,若由await修飾的部分為返回值骚露,則返回值類型必須聲明為Future類型

  Future getCamera() async {
    // 該方法調(diào)用異步方法availableCameras(),需要使用await來調(diào)用缚窿,且該方法需要使用async修飾棘幸,返回值為Future
    final cameras = await availableCameras();
    // Get a specific camera from the list of available cameras.
    final firstCamera = cameras.first;
  }

上面代碼中異步方法availableCameras()來自文件'package:camera/camera.dart';,

第三步:初始化CameraController

class TakePictureScreen extends StatefulWidget {
  final CameraDescription camera;

  const TakePictureScreen({
    Key key,
    @required this.camera,
  }) : super(key: key);

  @override
  TakePictureScreenState createState() => TakePictureScreenState();
}

class TakePictureScreenState extends State<TakePictureScreen> {
  // Add two variables to the state class to store the CameraController and
  // the Future.
  CameraController _controller;
  Future<void> _initializeControllerFuture;

  @override
  void initState() {
    super.initState();
    // To display the current output from the camera,
    // create a CameraController.
    _controller = CameraController(
      // Get a specific camera from the list of available cameras.
      widget.camera,
      // Define the resolution to use.
      ResolutionPreset.medium,
    );

    // Next, initialize the controller. This returns a Future.
    _initializeControllerFuture = _controller.initialize();
  }

  @override
  void dispose() {
    // Dispose of the controller when the widget is disposed.
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // Fill this out in the next steps.第四步中完成該方法
  }
}

第四步:使用CameraPreview顯示攝像頭獲取到的畫面

  //第三部中的build方法
  @override
  Widget build(BuildContext context) {
    if (!controller.value.isInitialized) {
      return new Container();
    }

    return new AspectRatio(
      aspectRatio: controller.value.aspectRatio,
      child: new CameraPreview(controller),
    );
  }

最后倦零,在main函數(shù)中異步獲取可用camera误续,并將該對象以構(gòu)造的方式傳入到camera_screen()中。繼續(xù)按照官方文檔中的第五六步可以實現(xiàn)拍照且扫茅,將照片顯示出來蹋嵌。

注意:前面講過Flutter類名和文件名無關(guān),且可以有多個一級類比如前面講到的WhatsappHome中的_WhatsAppHomeState葫隙,那么要在_WhatsAppHomeState中調(diào)用到WhatsappHome中的變量可以使用如下寫法

class _WhatsAppHomeState extends State<WhatsappHome> with SingleTickerProviderStateMixin {

通過查看State類的源碼可得知該類支持泛型栽烂,而State通常是作為StatefulWidget中createState的返回值出現(xiàn),故State中泛型通常傳入與之關(guān)聯(lián)的StatefulWidget恋脚。

@optionalTypeArgs
abstract class State<T extends StatefulWidget> extends Diagnosticable {...}

最后是Flutter中ListView的使用
在Flutter中滑動控件也叫ListView愕鼓,其相關(guān)的列表控件包括ScrollView以及直接或間接繼承自該類的子控件如CustomScrollView、GridView等
ListView官方文檔
通過閱讀文檔或者查看源碼可以發(fā)現(xiàn)慧起,ListView共四種用法,原文如下

///  1. The default constructor takes an explicit [List<Widget>] of children. This
///     constructor is appropriate for list views with a small number of
///     children because constructing the [List] requires doing work for every
///     child that could possibly be displayed in the list view instead of just
///     those children that are actually visible.默認(rèn)構(gòu)造方法册倒,WIdget數(shù)組即可蚓挤,適用數(shù)少量,固定的列表
///
///  2. The [ListView.builder] constructor takes an [IndexedWidgetBuilder], which
///     builds the children on demand. This constructor is appropriate for list views
///     with a large (or infinite) number of children because the builder is called
///     only for those children that are actually visible.根據(jù)需要構(gòu)建子項驻子。此構(gòu)造函數(shù)適用于具有大量(或無限)子項數(shù)的列表視圖
///
///  3. The [ListView.separated] constructor takes two [IndexedWidgetBuilder]s:
///     `itemBuilder` builds child items on demand, and `separatorBuilder`
///     similarly builds separator children which appear in between the child items.
///     This constructor is appropriate for list views with a fixed number of children.
///     按需構(gòu)建子項灿意,而separatorBuilder類似地構(gòu)建出現(xiàn)在子項之間的子項(分割線或者多層次的ListView)。此構(gòu)造函數(shù)適用于具有固定數(shù)量子項的列表視圖崇呵。
///
///  4. The [ListView.custom] constructor takes a [SliverChildDelegate], which provides
///     the ability to customize additional aspects of the child model. For example,
///     a [SliverChildDelegate] can control the algorithm used to estimate the
///     size of children that are not actually visible.
///     采用SliverChildDelegate缤剧,它提供了自定義子模型的其他方面的功能。例如域慷,SliverChildDelegate可以控制用于估計實際上不可見的子項大小的算法荒辕。
///     這個涉及到Sliver以及其衍生子類,動畫效果都很好犹褒,漸變漸隱之類的如SliverAppBar等抵窒。

下面是改造后的ChatScreen,其中方向由構(gòu)造方法中的參數(shù)決定叠骑,默認(rèn)豎向Axis scrollDirection = Axis.vertical,

class ChatScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new ChatScreenState();
  }
}

class ChatScreenState extends State<ChatScreen> {
  final List<String> entries = <String>["A", "B", "C", "A", "B", "C"];
  final List<int> background = <int>[50, 100, 200, 300, 400, 500];//顏色深度有一張表李皇,在一個叫Gallery的項目中找到的

  @override
  Widget build(BuildContext context) {
      //不同構(gòu)造方法
//    ListView.separated(itemBuilder: null, separatorBuilder: null, itemCount: null);
//    ListView.builder(itemBuilder: null);
//    ListView.custom(childrenDelegate: null);

    return new ListView.separated(
        padding: const EdgeInsets.all(5.0),
        itemCount: entries.length,
        separatorBuilder: (BuildContext context, int index) => const Divider(),//默認(rèn)分隔線
        itemBuilder: (BuildContext context, int index) {
          return Container(
            height: 100,
            color: Colors.red[background[index]],
            child: new Center(
              child: new Text("Item_${entries[index]}"),//支持字符串中直接拼接
            ),
          );
        });
  }
}

最終完成最上面圖中展示的效果。此項目來源于Flutter Example Apps,由于Flutter的跟新某些Api更新了所以重寫了下宙枷。

Demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掉房,一起剝皮案震驚了整個濱河市茧跋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卓囚,老刑警劉巖瘾杭,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異捍岳,居然都是意外死亡富寿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進(jìn)店門锣夹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來页徐,“玉大人,你說我怎么就攤上這事银萍”溆拢” “怎么了?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵贴唇,是天一觀的道長搀绣。 經(jīng)常有香客問我,道長戳气,這世上最難降的妖魔是什么链患? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮瓶您,結(jié)果婚禮上麻捻,老公的妹妹穿的比我還像新娘。我一直安慰自己呀袱,他們只是感情好贸毕,可當(dāng)我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著夜赵,像睡著了一般明棍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寇僧,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天摊腋,我揣著相機與錄音,去河邊找鬼嘁傀。 笑死歌豺,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的心包。 我是一名探鬼主播类咧,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痕惋?” 一聲冷哼從身側(cè)響起区宇,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎值戳,沒想到半個月后议谷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡堕虹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年卧晓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赴捞。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡逼裆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赦政,到底是詐尸還是另有隱情胜宇,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布恢着,位于F島的核電站桐愉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏掰派。R本人自食惡果不足惜从诲,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望靡羡。 院中可真熱鬧盏求,春花似錦、人聲如沸亿眠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纳像。三九已至,卻和暖如春拯勉,著一層夾襖步出監(jiān)牢的瞬間竟趾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工宫峦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留岔帽,地道東北人。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓导绷,卻偏偏與公主長得像犀勒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容

  • 作者: Mike Bluestein | 譯:孫印鳳 原文地址: [https://www.smashingmag...
    格老子閱讀 3,474評論 0 6
  • 英文官網(wǎng):https://flutter.io/中文網(wǎng):https://flutterchina.club/ 首先...
    超威藍(lán)貓l閱讀 2,824評論 1 3
  • 國慶后面兩天在家學(xué)習(xí)整理了一波flutter贾费,基本把能擼過能看到的代碼都過了一遍钦购,此文篇幅較長,建議保存(star...
    Nealyang閱讀 4,345評論 1 17
  • 原文:斷舍離最核心的思想就是“新陳代謝”褂萧。我們的居住空間處于代謝(交換)的狀態(tài)是最理想的押桃,因此,不斷地進(jìn)行“使用所...
    馨水宜蘭閱讀 197評論 0 1
  • 雖然感情比較細(xì)膩导犹,但是個活得比較糊涂的人唱凯。昨天公司調(diào)工資,突然間情緒爆發(fā)谎痢。人到中年磕昼,沒有余錢,辭了做了十年待遇還算...
    木辛隸水閱讀 285評論 0 4