Flutter學(xué)習(xí)

一怕午、基礎(chǔ)

1、Scaffold淹魄、Widget

  1. 在Flutter中郁惜,大多數(shù)東西都是widget,widget是控件實(shí)現(xiàn)的基本邏輯單位甲锡。widget是不可變的兆蕉,所以當(dāng)渲染試圖發(fā)生變化時(shí),F(xiàn)lutter會(huì)重建widget樹(shù)進(jìn)行更新缤沦。
  2. Scaffold 是 Material library 中提供的一個(gè)widget, 它提供了默認(rèn)的導(dǎo)航欄虎韵、標(biāo)題和包含主屏幕widget樹(shù)的body屬性。
  3. widget的主要工作是提供一個(gè)build()方法來(lái)描述如何根據(jù)其他較低級(jí)別的widget來(lái)顯示自己缸废。

StatefulWidget和StatelessWidget:
StatefulWidget在調(diào)用setState更新數(shù)據(jù)時(shí)包蓝,會(huì)觸發(fā)整個(gè)界面視圖的銷(xiāo)毀+重建(build)。所以能用StatelessWidget就不要用StatefulWidget企量。

2测萎、屬性

  1. Public和Private:聲明變量和方法時(shí),在前面加"_"表示【Private】梁钾,不加則默認(rèn)為【Public】

  2. const和final:都是修飾不可變的變量绳泉。const修飾的變量在【編譯】時(shí)能確定逊抡,final修飾的變量在運(yùn)行時(shí)確定姆泻。兩者一旦確定都不能改

3.存儲(chǔ)屬性和計(jì)算屬性

class ShopCart {
  String name; //存儲(chǔ)屬性
  price() {  //計(jì)算屬性
    return 1;
  }
}

3零酪、類(lèi)

繼承、接口拇勃、混入
class Point {
  var a = 0;
  var b = 0;
  void printInfo() => print('$a' + '$b');
}

//繼承:可以擁有父類(lèi)的屬性和方法實(shí)現(xiàn)
class PointA extends Point {
  var c = 0;
  @override
  void printInfo() => print('$a' + '$b' + '$c');
}

//接口實(shí)現(xiàn):獲取了Point的屬性和方法聲明四苇,需要自己給屬性賦值和實(shí)現(xiàn)方法
class PointB implements Point {
  var a = 0;
  var b = 0;
  void printInfo() => print('$a' + '$b');
}

//混入:擁有另一個(gè)類(lèi)的成員屬性和方法【實(shí)現(xiàn)】,并且能解決多繼承問(wèn)題
class PointC with Point {} //繼承PointA混入Point

注:如果一個(gè)類(lèi)A混入多個(gè)類(lèi)方咆,且這些多個(gè)類(lèi)中有同名的方法月腋,當(dāng)A調(diào)這個(gè)同名方法時(shí)會(huì)怎樣?
會(huì)調(diào)用最后一個(gè)with的類(lèi)的方法實(shí)現(xiàn)

4瓣赂、函數(shù)

  1. 構(gòu)造函數(shù)
class Point {
  num x,y,z;
  //初始化Point的時(shí)候給x和y賦值榆骚,等同于在函數(shù)體內(nèi)執(zhí)行:this.x = x;this.y = y; “: z = 0”的意思是,如果z沒(méi)有初始化賦值煌集,給他設(shè)置默認(rèn)值0
  Point(this.x, this.y) : z = 0;
  Point.bottom(num x) : this(x, 0); //重定向構(gòu)造函數(shù):傳一個(gè)數(shù)字妓肢,再調(diào)用 Point(this.x, this.y) 
  void printInfo() => print('($x,$y,$z)');
}
var pp = Point.bottom(100);
pp.printInfo(); //(100,0,0)

2.可選參數(shù)和可選命名參數(shù)

//可選命名參數(shù):調(diào)用函數(shù)時(shí),{} 里的參數(shù)可以不傳苫纤,但是傳的時(shí)候要有參數(shù)名
void enableFlags1({bool bold, bool hidden}) => print('$bold, $hidden');
void enableFlags2({bool bold, bool hidden = true}) => print('$bold, $hidden');
//可選參數(shù):調(diào)用函數(shù)時(shí)碉钠,[] 里的參數(shù)可以不傳,參數(shù)名頁(yè)不需要有
void enableFlags3(bool bold, [bool hidden]) => print('$bold, $hidden');
void enableFlags6(bool bold, [bool hidden = true]) => print('$bold, $hidden');
  
//可選命名參數(shù),帶參數(shù)名
enableFlags1(bold: true, hidden: false); //true, false
enableFlags1(bold: true); //true, null
enableFlags2(bold: false); //false, true
//可選參數(shù)卷拘,不帶參數(shù)名
enableFlags3(true, false); //true, false
enableFlags3(true); //true, null
enableFlags6(true); // true, true
enableFlags6(true, false); // true, false

5喊废、State生命周期

State生命周期
State生命周期分為3個(gè)階段:創(chuàng)建、更新栗弟、銷(xiāo)毀

創(chuàng)建:

  • initState:只調(diào)用一次污筷,負(fù)責(zé)初始化工作
  • didChangeDependencies:處理State對(duì)象依賴(lài)關(guān)系變化
  • build:負(fù)責(zé)構(gòu)建視圖

更新:

  • setState:數(shù)據(jù)變化時(shí),更新UI
  • didChangeDependencies:State對(duì)象依賴(lài)關(guān)系變化后會(huì)出發(fā)此方法觸發(fā)build乍赫。什么時(shí)候依賴(lài)會(huì)發(fā)生變化呢颓屑?典型的場(chǎng)景是應(yīng)用主題改變時(shí)執(zhí)行。
  • didUpdateWidget:父Widget狀態(tài)發(fā)生變化重建時(shí)或熱重載時(shí)調(diào)用耿焊。

銷(xiāo)毀:

  • deactivate和dispose:組件移除或頁(yè)面銷(xiāo)毀時(shí)系統(tǒng)會(huì)調(diào)用這兩個(gè)方法

如圖:左邊展示的是父Widget變化時(shí)忧勿,子Widget的變化。中間和右邊是push和pop時(shí)新舊Widget的變化

state生命周期

6较锡、跨組建傳遞數(shù)據(jù)

Notification:只能在父子Widget之間傳遞
class AboutUsPageState extends State<AboutUsPage> {
  String _msg = '通知:';

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    //接收通知
    return NotificationListener<CustomNotification>(
      onNotification: (notification) {
        setState(() {
          _msg += notification.msg;
        });
      },
      child: Column(
        children: <Widget>[Text(_msg), NotiPoter()],
      ),
    );
  }
}
//寫(xiě)一個(gè)繼承自【Notification】的類(lèi)
class CustomNotification extends Notification {
  final String msg;
  CustomNotification(this.msg);
}
//發(fā)送通知
class NotiPoter extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      onPressed: () {
        CustomNotification('哈哈哈').dispatch(context);
      },
      child: Text('發(fā)送通知'),
    );
  }
}
EventBus:無(wú)需父子Widget關(guān)系月杉,可支持任意對(duì)象傳遞
//創(chuàng)建一個(gè)EventBus對(duì)象
EventBus eventBus = new EventBus();

class AboutUsPageState extends State<AboutUsPage> {
  String _msg = '通知:';
  @override
  void initState() {
    //監(jiān)聽(tīng)通知
    eventBus.on<CustomBus>().listen((event) {
      setState(() {
        _msg += event.notiMsg;
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[PushNoti(), Text(_msg)],
    );
  }
  
  @override
  void dispose() {
    //銷(xiāo)毀通知
    eventBus.destroy();
    super.dispose();
  }
}

//自定義任何類(lèi)
class CustomBus {
  String notiMsg;
  CustomBus(this.notiMsg);
}

class PushNoti extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return RaisedButton(
      onPressed: () {
        //發(fā)送通知
        eventBus.fire(CustomBus('呵呵呵呵呵'));
      },
      child: Text('點(diǎn)擊發(fā)送通知'),
    );
  }
}

7、路由

基本路由
//ProductDetail為跳轉(zhuǎn)到的類(lèi)钩杰,可穿參
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => ProductDetail(item: i)),
);
命名路由
//注冊(cè)
routes: <String, WidgetBuilder>{
  "news_page" : (BuildContext context) => NewsPage()
},

//跳轉(zhuǎn)
Navigator.pushNamed(context, 'home_page', arguments: '哈哈哈哈哈');

//跳轉(zhuǎn)到的頁(yè)面接受參數(shù)
Widget build(BuildContext context) {
  //接收參數(shù)
  String msg = ModalRoute.of(context).settings.arguments as String;
  print(msg);
  return Scaffold(
    body: ListView(
      children: <Widget>[
        BannerWidget(),
        HomeProductPage(listData),
      ],
    ),
  );
}

8纫塌、Event Loop機(jī)制

Dart是單線(xiàn)程的,但也支持異步讲弄。為什么措左?

有個(gè)大前提,APP大多時(shí)間都在等待避除,比如等待網(wǎng)絡(luò)請(qǐng)求返回怎披,等待用戶(hù)點(diǎn)擊等胸嘁。等待的行為并不阻塞線(xiàn)程,所以單線(xiàn)程在等待時(shí)可以做很多事凉逛,等真正需要響應(yīng)結(jié)果了再去做對(duì)應(yīng)的處理性宏。等待這個(gè)行為時(shí)通過(guò)Event Loop驅(qū)動(dòng)的。在Dart中有兩個(gè)隊(duì)列:事件隊(duì)列(Event Queue)状飞、微任務(wù)隊(duì)列(Microtask Queue)毫胜。

  • 微任務(wù)隊(duì)列表示一個(gè)短時(shí)間內(nèi)就完成的異步任務(wù)(比如,手勢(shì)識(shí)別诬辈、文字輸入酵使、滾動(dòng)視圖、保存頁(yè)面等)焙糟,在事件循環(huán)中優(yōu)先級(jí)是最高的凝化。

  • 事件隊(duì)列優(yōu)先級(jí)較低,比如I/O酬荞、繪制搓劫、定時(shí)器等

如何實(shí)現(xiàn)異步?

把一個(gè)函數(shù)體放入Future混巧,就完成了從同步到異步的包裝枪向。Flutter還提供了鏈?zhǔn)秸{(diào)用,在異步執(zhí)行完后依次執(zhí)行鏈路上的其他函數(shù)體咧党。

Future(() => print('1111111'))
        .then((_) => print('2222222'))
        .then((_) => print('3333333')); //打印完1后秘蛔,一次打印2和3

將函數(shù)放入Future后,Dart會(huì)將異步任務(wù)放入事件隊(duì)列傍衡,【后續(xù)的代碼正常同步執(zhí)行】深员。同步代碼執(zhí)行完后,事件隊(duì)列會(huì)根據(jù)【事件加入的順序】依次取出事件蛙埂,然后執(zhí)行Future函數(shù)體及后續(xù)的then倦畅。

執(zhí)行順序
異步函數(shù)

對(duì)于一個(gè)異步函數(shù)來(lái)說(shuō),結(jié)果不會(huì)立即返回绣的,因此需要返回一個(gè)Future對(duì)象提供給調(diào)用者叠赐。調(diào)用者可以在Future對(duì)象注冊(cè)一個(gè)then等Future執(zhí)行完在進(jìn)行異步處理,或者使用await關(guān)鍵字一直等待Future執(zhí)行體結(jié)束屡江。

使用await一直等待芭概,常見(jiàn)于網(wǎng)絡(luò)請(qǐng)求。函數(shù)體用async關(guān)鍵字惩嘉,調(diào)用時(shí)使用await關(guān)鍵字

Future<FinancialTabEntity> getFinancialTab() async {
    Map args = Map();
    args["action"] = "50000";
    args["path"] = "/inst_plat_app/v_1.8.0/head_tabs";

    Map result = await HtflutterWebenginePlugin.sendReqXMLRequest(args);

    String errorNo = result["ERRORNO"];
    if (int.parse(errorNo) != 100) {
      return null;
    }

    Map resultDic = jsonDecode(result["BINDATA"]);
    if (int.parse(resultDic['code']) != 0) {
      return null;
    }

    final model = FinancialTabEntity.fromJson(resultDic);
    return model;
  }
_financialTabEntity = await _apiClient.getFinancialTab();//調(diào)用時(shí)使用await關(guān)鍵字

函數(shù)體為什么要加async關(guān)鍵字罢洲?

因?yàn)閍wait的作用不是阻塞等待,而是異步等待(不會(huì)影響其他操作)文黎。Dart會(huì)將調(diào)用函數(shù)體的函數(shù)也視為異步函數(shù)惹苗,將等待語(yǔ)句放入Event Queue中殿较,一旦有了結(jié)果,Event Loop就把它從Event Queue中取出執(zhí)行鸽粉。

Isolate

盡管Dart基于單線(xiàn)程模型,也提供了多線(xiàn)程機(jī)制抓艳,即Isolate触机。Isolate之間不共享資源,只依靠消息機(jī)制通信玷或,因此也沒(méi)有資源搶占問(wèn)題儡首。

 Isolate isolate;

  start() async {
    ReceivePort receivePort = ReceivePort(); //創(chuàng)建管道
    isolate = await Isolate.spawn(getMsg, receivePort.sendPort); //將管道作為參數(shù)傳入

    receivePort.listen((data) {
      print('$data');
      receivePort.close();
    });
  }

  getMsg(sendPort) => sendPort.send("Hello");

Isolate通過(guò)發(fā)送管道(SendPort)實(shí)現(xiàn)消息通信機(jī)制。在啟動(dòng)Isolate時(shí)偏友,將發(fā)送管道作為參數(shù)傳給他蔬胯,這樣并發(fā)Isolate就可以在任務(wù)執(zhí)行完后利用發(fā)送管道給我們發(fā)消息了。

9位他、本地存儲(chǔ)

文件存儲(chǔ)

需要安裝:path_provider: ^xxx

//創(chuàng)建/獲取文件路徑
Future<File> _localFile() async {
  String dir = (await getApplicationDocumentsDirectory()).path;
  return new File('$dir/test.txt');
}
//將字符串寫(xiě)入文件
Future<File> writeString(String string) async {
  final file = await _localFile();
  return file.writeAsString(string);
}
//讀取文件里的字符串
Future<String> readString() async {
  try {
    final file = await _localFile();
    String str = await file.readAsString();
    print(str);
  } catch (e) {
    return '';
  }
}
SharedPreferences (相當(dāng)于NSUserDefaults)

需要安裝:shared_preferences: ^xxx

Future<void> _saveString(String str) async {
  SharedPreferences p = await SharedPreferences.getInstance();
  p.setString('orange', str);
}

Future<String> _readString() async {
  SharedPreferences p = await SharedPreferences.getInstance();
  String str = p.getString('orange');
  print(str);
}
數(shù)據(jù)庫(kù)

和iOS數(shù)據(jù)庫(kù)用法類(lèi)似氛濒,不再舉例

10、Flutter與原生交互

方法通道(Method Channel)

由于Flutter只接管了渲染層鹅髓,因此當(dāng)需要用到系統(tǒng)的底層功能如:藍(lán)牙舞竿、相機(jī),或者用到原生比較成熟的框架時(shí)窿冯,可以用方法通道(Method Channel)與原生交互骗奖。

Flutter端代碼

//創(chuàng)建方法通道
const platform = MethodChannel('orangeChannel');
handleClick() async {
  try {
    platform.invokeMethod('orangeMethod'); //命名方法名
  } catch (e) {
    print('error');
  }
}

onTap: () {
  handleClick();
}

iOS端代碼.

#import "AppDelegate.h"
#import "FlutterPluginRegistrant/GeneratedPluginRegistrant.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    FlutterViewController *controller = (FlutterViewController *)self.window.rootViewController;
    FlutterMethodChannel *channel = [FlutterMethodChannel methodChannelWithName:@"orangeChannel" binaryMessenger:controller];
    //監(jiān)聽(tīng)Flutter端方法調(diào)用
    [channel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        if ([call.method isEqualToString:@"orangeMethod"]) {
            NSLog(@"okokokokokokokok");
            result(@1);
        }else {
            result(FlutterMethodNotImplemented);
        }
    }];
    
  [GeneratedPluginRegistrant registerWithRegistry:self];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end
平臺(tái)視圖

將原生視圖嵌入到Flutter里面

Flutter端

UiKitView(
  viewType: 'orangeView',
)

iOS端

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    NSObject<FlutterPluginRegistrar> *registrar = [self registrarForPlugin:@"samples.chenhang/native_views"];
    OrangeFactory *viewFactory = [[OrangeFactory alloc] initWithMessenger:[registrar messenger]];
    [registrar registerViewFactory:viewFactory withId:@"orangeView"];
    
  [GeneratedPluginRegistrant registerWithRegistry:self];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

OrangeFactory.h

#import <Foundation/Foundation.h>
#import <Flutter/Flutter.h>

NS_ASSUME_NONNULL_BEGIN

@interface OrangeFactory : NSObject
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger> *)messager;
@end

@interface OrangeViewControl : NSObject<FlutterPlatformView>
- (instancetype)initWithWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args;
@end

NS_ASSUME_NONNULL_END

OrangeFactory.m

#import "OrangeFactory.h"

@interface OrangeFactory ()<FlutterPlatformViewFactory>

@end

@implementation OrangeFactory{
    NSObject<FlutterBinaryMessenger> *_messenger;
}

- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger> *)messager {
    self = [super init];
    if (self) {
        _messenger = messager;
    }
    return self;
}

-(NSObject<FlutterMessageCodec> *)createArgsCodec{
    return [FlutterStandardMessageCodec sharedInstance];
}

//創(chuàng)建原生視圖封裝實(shí)例
- (NSObject<FlutterPlatformView> *)createWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args {
    OrangeViewControl *ctl = [[OrangeViewControl alloc] initWithWithFrame:frame viewIdentifier:viewId arguments:args];
    return ctl;
}
@end


//iOS視圖封裝類(lèi)
@interface OrangeViewControl ()

@end

@implementation OrangeViewControl{
    UIView *_tempView;
}
//創(chuàng)建原生視圖
- (instancetype)initWithWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id)args {
    if ([super init]) {
        _tempView = [[UIView alloc] init];
        _tempView.backgroundColor = [UIColor orangeColor];
    }
    return self;
}

- (UIView *)view {
    return _tempView;
}

@end

二、布局

1醒串、MainAxisAlignment和CrossAxisAlignment

MainAxisAlignment:

1执桌、Row里面代表水平軸

2、Column里面代表垂直軸

CrossAxisAlignment:

1芜赌、Row里面代表垂直軸

2仰挣、Column里面代表水平軸

  • CrossAxisAlignment.stretch

2、MainAxisSize屬性

Row和Column的主軸會(huì)盡可能大缠沈,cross軸會(huì)自適應(yīng)子空間最大的值,如果想要它的主軸也自適應(yīng)它的子控件椎木,就用MainAxisSize.min。

3博烂、Flutter Container 的alignment屬性

Container(
  alignment: Alignment(0.0, 1.0),
  child:Text('ss')
)

如下圖:左上角為(-1, -1)香椎,右下角為(1, 1)

4、Flex布局

5禽篱、Stack與Positioned

Stack提供了布局的容器畜伐,Positioned提供了子Widget設(shè)置坐標(biāo)位置的能力
Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        Container(
          color: Colors.orange,
          width: 300,
          height: 300,
        ),
        Container(
          color: Colors.blue,
          width: 150,
          height: 200,
        ),
        //Positioned包裹的控件會(huì)在上面兩個(gè)范圍大的Container中布局
        Positioned(
          left: 10,
          right: 20,
          top: 30,
          height: 100,
          child: Container(
            color: Colors.green,
          ),
        ),

        Positioned(
          left: 100,
          right: 50,
          top: 230,
          bottom: 10,
          child: Container(
            color: Colors.red,
          ),
        ),
      ],
    );
  }

6、ListView滾動(dòng)監(jiān)聽(tīng)

ScrollController方式:將ScrollController對(duì)象賦值給ListView屬性

class AboutUsPageState extends State<AboutUsPage> {
  ScrollController _controller;
  bool _isTop;

  @override
  void initState() {
    //利用ScrollController進(jìn)行監(jiān)聽(tīng)
    _controller = ScrollController();
    _controller.addListener(() {
      print(_controller.offset);
      if (_controller.offset > 100) {
        _isTop = true;
      } else {
        _isTop = false;
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        RaisedButton(
          onPressed: (() {
            if (_isTop == true) {
              //ScrollController也能控制ListView滾動(dòng)
              _controller.animateTo(0,
                  duration: Duration(microseconds: 200), curve: Curves.ease);
            }
          }),
          child: Text("Top"),
        ),
        Expanded(
          child: ListView.builder(
              controller: _controller, //將ScrollController對(duì)象添加到ListView
              itemCount: 50,
              itemBuilder: (context, index) {
                return ListTile(title: Text('Item #$index'));
              }),
        )
      ],
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    // TODO: implement dispose
    super.dispose();
  }
}

NotificationListener方式:將NotificationListener作為L(zhǎng)istView的父Widget可監(jiān)聽(tīng)其滾動(dòng)狀態(tài)

class AboutUsPageState extends State<AboutUsPage> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return NotificationListener(
      onNotification: (notify) {
        if (notify is ScrollStartNotification) {
          //滾動(dòng)開(kāi)始
          print('Scroll Start');
        } else if (notify is ScrollUpdateNotification) {
          //滾動(dòng)更新
          print('Scroll Update');
        } else if (notify is ScrollEndNotification) {
          //滾動(dòng)結(jié)束
          print('Scroll End');
        }
      },
      child: ListView.builder(
          itemCount: 50,
          itemBuilder: (context, index) {
            return ListTile(title: Text('Item #$index'));
          }),
    );
  }
}

7躺率、繪圖

Flutter中 我們想要繪圖首先要寫(xiě)個(gè)類(lèi)XXX繼承自 CustomPainter玛界,然后在類(lèi)XXX里面實(shí)現(xiàn) Paint(畫(huà)筆)和繪圖方法:void paint(Canvas canvas, Size size) {} 万矾。最后我們將這個(gè)類(lèi)XXX實(shí)例成對(duì)象賦值給 CustomPaint 繪圖容器類(lèi)里的painter屬性。

class AboutUsPageState extends State<AboutUsPage> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    //CustomPaint 是一個(gè)裝繪圖的容器
    return CustomPaint(
      size: Size(300, 300),
      //把我們寫(xiě)的 CustomPainter 子類(lèi)的實(shí)例對(duì)象賦值給他
      painter: DrawPicture(),
    );
  }
}

//想要繪制圖要寫(xiě)一個(gè)類(lèi)繼承自 CustomPainter 
class DrawPicture extends CustomPainter {
  //畫(huà)筆:可以設(shè)置畫(huà)筆顏色慎框,粗細(xì)等
  Paint getColoredPaint(Color color) {
    Paint paint = Paint();
    paint.color = color;
    paint.strokeWidth = 2;
    return paint;
  }

  //在這里畫(huà)圖
  @override
  void paint(Canvas canvas, Size size) {
    // canvas.drawLine(
    //     //繪制一條直線(xiàn)
    //     Offset(10, 10),
    //     Offset(100, 100),
    //     getColoredPaint(Colors.red));

    canvas.drawPath( //繪制?
        Path()
          ..moveTo(20, 100)
          ..lineTo(200, 100)
          ..lineTo(60, 220)
          ..lineTo(110, 20)
          ..lineTo(150, 220)
          ..lineTo(20, 100)
          ..close(),
        getColoredPaint(Colors.red));
  }

  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末良狈,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子笨枯,更是在濱河造成了極大的恐慌薪丁,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件馅精,死亡現(xiàn)場(chǎng)離奇詭異严嗜,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)洲敢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)漫玄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人压彭,你說(shuō)我怎么就攤上這事睦优。” “怎么了壮不?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵刨秆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我忆畅,道長(zhǎng)衡未,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任家凯,我火速辦了婚禮缓醋,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绊诲。我一直安慰自己送粱,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布掂之。 她就那樣靜靜地躺著抗俄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪世舰。 梳的紋絲不亂的頭發(fā)上动雹,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音跟压,去河邊找鬼胰蝠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茸塞。 我是一名探鬼主播躲庄,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼钾虐!你這毒婦竟也來(lái)了噪窘?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤效扫,失蹤者是張志新(化名)和其女友劉穎倔监,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體荡短,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丐枉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年哆键,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掘托。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡籍嘹,死狀恐怖闪盔,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辱士,我是刑警寧澤泪掀,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站颂碘,受9級(jí)特大地震影響异赫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜头岔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一塔拳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧峡竣,春花似錦靠抑、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至类浪,卻和暖如春载城,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背费就。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工个曙, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓垦搬,卻偏偏與公主長(zhǎng)得像呼寸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子猴贰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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