安装与运行 Windows 下载安装包:https://flutter.io/sdk-archive/#windows 
临时镜像:
1 2 export PUB_HOSTED_URL=https://pub.flutter-io.cn export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn 
 
在Flutter安装目录的flutter文件下找到flutter_console.bat,双击运行并启动flutter命令行 。
将 flutter\bin 添加到环境变量 Path。
编译桌面端 
安装go:https://studygolang.com/dl 
 
安装hover
1 2 3 go env -w GOPROXY=https://goproxy.cn set GO111MODULE=on go get -u -a github.com/go-flutter-desktop/hover 
 
如果出现如下错误提示,则需要升级你的go版本,最低支持1.12:
1 cmdApp.ProcessState.ExitCode undefined (type *os.ProcessState has no field or method ExitCode) 
 
还需要确保你的电脑具有GLFW的依赖:看这里https://github.com/go-flutter-desktop/hover ,或者这里https://www.glfw.org/docs/latest/compile.html#compile_deps 。
 
编译 第一次将hover应用于项目时,需要初始化桌面项目。hover init需要项目路径,通常是你在GitHub或者托管到git服务上的项目路径。如:
1 hover init github.com/iwxyi/QQ-Notification_Reply 
 
这路径不重要以后可以随时更改 执行初始化之后将在项目中创建desktop并添加样板文件,如go代码和默认图标。  默认flutter项目是以main.dart作为入口文件,编译桌面应用,你需要新建一个名为main_desktop.dart的文件,并且在runApp(..);之前加上以下代码:
1 debugDefaultTargetPlatformOverride =  TargetPlatform .fuchsia; 
 
然后执行hover run启动程序即可,项目启动后,在命令行按r可以热重载。  要构建一个独立的应用程序可以使用hover build命令。输出在desktop/build/outputs/Windows目录中。
 
 
第一个应用 本笔记来源:Flutter中文网 
 
1、Hello World lib/main.dart文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import  'package:flutter/material.dart' ;void  main() => runApp(new  MyApp());class  MyApp  extends  StatelessWidget   {  @override    Widget build(BuildContext context) {     return  new  MaterialApp(       title: 'Welcome to Flutter' ,       home: new  Scaffold(          appBar: new  AppBar(           title: new  Text('Welcome to Flutter' ),         ),         body: new  Center(           child: new  Text('Hello World' ),         ),       ),     );   } } 
 
2、Package pubspec.yaml
1 2 3 4 5 6 dependencies:   flutter:     sdk:  flutter   cupertino_icons:  ^0.1.0   english_words:  ^3.1.0 
 
点击右上角的 Packages get,会自动下载依赖包
lib/main.dart
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import  'package:flutter/material.dart' ;import  'package:english_words/english_words.dart' ;void  main() => runApp(new  MyApp());class  MyApp  extends  StatelessWidget   {  @override    Widget build(BuildContext context) {     final  wordPair = new  WordPair.random();     return  new  MaterialApp(       title: 'Welcome to Flutter' ,       home: new  Scaffold(         appBar: new  AppBar(           title: new  Text('Welcome to Flutter' ),         ),         body: new  Center(           child: new  Text(wordPair.asPascalCase),         ),       ),     );   } } 
 
3、State StatefulWidget类本身是不变的,但是 State类在widget生命周期中始终存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import  'package:flutter/material.dart' ;import  'package:english_words/english_words.dart' ;void  main() => runApp(new  MyApp());class  MyApp  extends  StatelessWidget   {  @override    Widget build(BuildContext context) {     final  wordPair = new  WordPair.random();     return  new  MaterialApp(       title: 'Welcome to Flutter' ,       home: new  RandomWords(),     );   } } class  RandomWords  extends  StatefulWidget   {  @override    createState() => new  RandomWordsState(); } class  RandomWordsState  extends  State <RandomWords >  {  @override    Widget build(BuildContext context) {     final  wordPair = new  WordPair.random();     return  new  Text(wordPair.asPascalCase);   } } 
 
4、ListView ListView设置方法:ListView.builder( itemBuilder:(context, i) { } )
1 2 3 4 5 6 7 8 9 10 Widget _buildList() {   return  new  ListView.builder(itemBuilder: (context, i){          return  new  ListTile(        title: new  Text("text" ),       trailing: new  Icon(Icons.favorite),       onTap: (){},     );   }); } 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class  RandomWordsState  extends  State <RandomWords >  {  final  _suggestions = <WordPair>[];       @override    Widget build(BuildContext context) {     return  new  Scaffold(        appBar: new  AppBar(         title: new  Text('Flutter' ),       ),       body: _buildSuggestions(),     );   }      Widget _buildSuggestions() {                    return  new  ListView.builder(       padding: const  EdgeInsets.all(16.0 ),                                   itemBuilder: (context, i) {                  if  (i.isOdd) return  new  Divider();                           final  index = i ~/ 2 ;                  if  (index >= _suggestions.length) {                      _suggestions.addAll(generateWordPairs().take(10 ));         }         return  _buildRow(_suggestions[index]);       },     );   }   Widget _buildRow(WordPair pair) {     return  new  ListTile(       title: new  Text(         pair.asPascalCase,         style: new  TextStyle(fontSize: 18.0 ),       ),     );   } } 
 
5、onTap 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class  RandomWordsState  extends  State <RandomWords >  {  final  _suggestions = <WordPair>[];   final  _saved = new  Set <WordPair>();             Widget _buildRow(WordPair pair) {     final  alreadySaved = _saved.contains(pair);     return  new  ListTile(       title: new  Text(         pair.asPascalCase,         style: new  TextStyle(fontSize: 18.0 ),       ),       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);               }         });       },     );   } } 
 
6、Router 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class  RandomWordsState  extends  State <RandomWords >  {  final  _suggestions = <WordPair>[];   final  _saved = new  Set <WordPair>();   final  _biggerFont = const  TextStyle(fontSize: 18.0 );      @override    Widget build(BuildContext context) {     return  new  Scaffold(       appBar: new  AppBar(         title: new  Text('Flutter' ),                  actions: <Widget>[           new  IconButton(icon: new  Icon(Icons.list), onPressed: _pushSaved),         ],       ),       body: _buildSuggestions(),     );   }           void  _pushSaved() {          Navigator.of(context).push(              new  MaterialPageRoute(builder: (context){                  final  tiles = _saved.map((pair) {           return  new  ListTile(             title: new  Text(               pair.asPascalCase,               style: _biggerFont,             ),           );         });                  final  divided = ListTile             .divideTiles(             context: context,             tiles: tiles         ).toList();                         return  new  Scaffold(           appBar: new  AppBar(             title: new  Text('Saved Suggestions' ),           ),           body: new  ListView(children: divided),         );       }),     );   } } 
 
7、Theme 1 2 3 4 5 6 7 8 9 10 11 12 13 class  MyApp  extends  StatelessWidget   {  @override    Widget build(BuildContext context) {     return  new  MaterialApp(       title: 'Welcome to Flutter' ,              theme: new  ThemeData(         primaryColor: Colors.white,       ),       home: new  RandomWords(),     );   } } 
 
完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 import  'package:flutter/material.dart' ;import  'package:english_words/english_words.dart' ;void  main() => runApp(new  MyApp());class  MyApp  extends  StatelessWidget   {  @override    Widget build(BuildContext context) {     return  new  MaterialApp(       title: 'Welcome to Flutter' ,              theme: new  ThemeData(         primaryColor: Colors.white,       ),       home: new  RandomWords(),     );   } } class  RandomWords  extends  StatefulWidget   {  @override    createState() => new  RandomWordsState(); } class  RandomWordsState  extends  State <RandomWords >  {     final  _suggestions = <WordPair>[];     final  _saved = new  Set <WordPair>();    final  _biggerFont = const  TextStyle(fontSize: 18.0 );      @override    Widget build(BuildContext context) {     return  new  Scaffold(         appBar: new  AppBar(         title: new  Text('Flutter' ),                  actions: <Widget>[           new  IconButton(icon: new  Icon(Icons.list), onPressed: _pushSaved),         ],       ),       body: _buildSuggestions(),     );   }      Widget _buildSuggestions() {                    return  new  ListView.builder(       padding: const  EdgeInsets.all(16.0 ),                                   itemBuilder: (context, i) {                  if  (i.isOdd) return  new  Divider();                           final  index = i ~/ 2 ;                  if  (index >= _suggestions.length) {                      _suggestions.addAll(generateWordPairs().take(10 ));         }         return  _buildRow(_suggestions[index]);       },     );   }      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);               }         });       },     );   }      void  _pushSaved() {          Navigator.of(context).push(              new  MaterialPageRoute(builder: (context){                  final  tiles = _saved.map((pair) {           return  new  ListTile(             title: new  Text(               pair.asPascalCase,               style: _biggerFont,             ),           );         });                  final  divided = ListTile             .divideTiles(             context: context,             tiles: tiles         ).toList();                         return  new  Scaffold(           appBar: new  AppBar(             title: new  Text('Saved Suggestions' ),           ),           body: new  ListView(children: divided),         );       }),     );   } } 
 
Widget目录 
Text 
Row、Column 
Stack 
Container:矩形视觉元素 
 
处理手势 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class  MyButton  extends  StatelessWidget   {  @override    Widget build(BuildContext context) {          return  new  GestureDetector(        onTap: () {         print ('MyButton was tapped!' );       },       child: new  Container(         height: 36.0 ,         padding: const  EdgeInsets.all(8.0 ),         margin: const  EdgeInsets.symmetric(horizontal: 8.0 ),         decoration: new  BoxDecoration(           borderRadius: new  BorderRadius.circular(5.0 ),           color: Colors.lightGreen[500 ],         ),         child: new  Center(           child: new  Text('Engage' ),         ),       ),     );   } } 
 
每次点击“Increment”按钮,显示的Count+1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class  _CounterState  extends  State <Counter >  {  int  _counter = 0 ;      void  _increment() {     setState(() {              _counter++;     });   }         @override    Widget build(BuildContext context) {               return  new  Row(       children: <Widget>[         new  RaisedButton(           onPressed: _increment,           child: new  Text('Increment' ),         ),         new  Text('Count: $_counter ' ),       ],     );   } } 
 
Stateless+Stateful 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import  'package:flutter/material.dart' ;void  main() {  runApp(new  MaterialApp(     title: 'Flutter Tutorial' ,     home: new  Counter(),   )); } class  CounterDisplay  extends  StatelessWidget   {  CounterDisplay({this .count});      final  int  count;      @override    Widget build(BuildContext context) {     return  new  Text('Count: $count ' );   } } class  CounterIncrementor  extends  StatelessWidget   {  CounterIncrementor({this .onPressed});      final  VoidCallback onPressed;       @override    Widget build(BuildContext context) {     return  new  RaisedButton(       onPressed: onPressed,       child: new  Text('Increment' ),     );   } } class  Counter  extends  StatefulWidget   {  @override    _CounterState createState() => new  _CounterState(); } class  _CounterState  extends  State <Counter >  {  int  _counter = 0 ;      void  _increment() {     setState(() {       ++_counter;     });   }      @override    Widget build(BuildContext context) {     return  new  Row(children: <Widget>[       new  CounterIncrementor(onPressed: _increment),       new  CounterDisplay(count: _counter),     ]);   } } 
 
购物车示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import  'package:flutter/material.dart' ;class  Product   {  const  Product({this .name});   final  String  name; } typedef  void  CartChangedCallback(Product product, bool  inCart);class  ShoppingListItem  extends  StatelessWidget   {  ShoppingListItem({Product product, this .inCart, this .onCartChanged})       : product = product,         super (key: new  ObjectKey(product));       final  Product product;   final  bool  inCart;   final  CartChangedCallback onCartChanged;      Color _getColor(BuildContext context) {     return  inCart ? Colors.black54 : Theme.of(context).primaryColor;   }      TextStyle _getTextStyle(BuildContext context) {     if  (!inCart) return  null ;          return  new  TextStyle(       color: Colors.black54,       decoration: TextDecoration.lineThrough,     );   }      @override    Widget build(BuildContext context) {     return  new  ListTile(       onTap: () {         onCartChanged(product, !inCart);       },       leading: new  CircleAvatar(          backgroundColor: _getColor(context),         child: new  Text(product.name[0 ]),        ),       title: new  Text(product.name, style: _getTextStyle(context)),     );   } } 
 
该ShoppingListItem widget是无状态的。它将其在构造函数中接收到的值存储在final成员变量中,然后在build函数中使用它们。 例如,inCart布尔值表示在两种视觉展示效果之间切换:一个使用当前主题的主色,另一个使用灰色。
当用户点击列表项时,widget不会直接修改其inCart的值。相反,widget会调用其父widget给它的onCartChanged回调函数。 此模式可让您在widget层次结构中存储更高的状态,从而使状态持续更长的时间。在极端情况下,存储传给runApp 应用程序的widget的状态将在的整个生命周期中持续存在。
当父项收到onCartChanged回调时,父项将更新其内部状态,这将触发父项使用新inCart值重建ShoppingListItem新实例。 虽然父项ShoppingListItem在重建时创建了一个新实例,但该操作开销很小 ,因为Flutter框架会将新构建的widget与先前构建的widget进行比较,并仅将差异部分应用于底层RenderObject 。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 class  ShoppingList  extends  StatefulWidget   {  ShoppingList({Key key, this .products}) : super (key: key);      final  List <Product> products;       @override    _ShoppingListState createState() => new  _ShoppingListState(); } class  _ShoppingListState  extends  State <ShoppingList >  {  Set <Product> _shoppingCart = new  Set <Product>();          void  _handleCartChanged(Product product, bool  inCart) {     setState(() {        if  (inCart)         _shoppingCart.add(product);       else          _shoppingCart.remove(product);     });   }      @override    Widget build(BuildContext context) {     return  new  Scaffold(       appBar: new  AppBar(         title: new  Text('Shopping List' ),       ),       body: new  ListView(         padding: new  EdgeInsets.symmetric(vertical: 8.0 ),                           children: widget.products.map((Product product) {           return  new  ShoppingListItem(             product: product,             inCart: _shoppingCart.contains(product),             onCartChanged: _handleCartChanged,           );         }).toList(),       ),     );   } } void  main() {  runApp(new  MaterialApp(     title: 'Shopping App' ,     home: new  ShoppingList(       products: <Product>[          new  Product(name: 'Eggs' ),         new  Product(name: 'Flour' ),         new  Product(name: 'Chocolate chips' ),       ],     ),   )); } 
 
当这个widget的父级重建时,父级将创建一个新的ShoppingList实例,但是Flutter框架将重用已经在树中的_ShoppingListState实例,而不是再次调用 createState 创建一个新的。
要访问当前ShoppingList的属性,_ShoppingListState可以使用它的widget属性。 如果父级重建并创建一个新的ShoppingList,那么 _ShoppingListState也将用新的widget值重建。如果希望在widget属性更改时收到通知,则可以覆盖didUpdateWidget函数,以便将旧的oldWidget与当前widget进行比较。
生命周期事件 在StatefulWidget调用createState之后,框架将新的状态对象插入树中,然后调用状态对象的initState 。 子类化State可以重写initState ,以完成仅需要执行一次的工作。 
Key 使用key来控制框架将在widget重建时与哪些其他widget匹配。要求两个widget具有相同的key 和runtimeType 。
如果没有key,当前构建中的第一个条目将始终与前一个构建中的第一个条目同步 
通过给列表中的每个条目分配为“语义” key,无限列表可以更高效,因为框架将同步条目与匹配的语义key并因此具有相似(或相同)的可视外观。此外,语义上同步条目意味着在有状态子widget中,保留的状态将附加到相同的语义条目上,而不是附加到相同数字位置上的条目。 
 
全局 Key 使用全局key来唯一标识子widget。全局key在整个widget层次结构中必须是全局唯一的,这与局部key不同,后者只需要在同级中唯一。由于它们是全局唯一的,因此可以使用全局key来检索与widget关联的状态。
 
零散控件笔记 详细说明:NestedScrollView类 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 final  _tabs = ['Tab1' , 'Tab2' , 'Tab3' ];DefaultTabController(   length: _tabs.length,   child: NestedScrollView(      headerSliverBuilder: (BuildContext context, bool  innerBoxIsScrolled) {       return  <Widget>[         SliverOverlapAbsorber(            handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),           child: SliverAppBar(              title: const  Text('Books' ),              pinned: true ,              expandedHeight: 150.0 ,              forceElevated: innerBoxIsScrolled,             bottom: TabBar(                tabs: _tabs.map((String  name) => Tab(text: name)).toList(),             ),           ),         ),       ];     },     body: TabBarView(        children: _tabs.map((String  name) {          return  SafeArea(           top: false ,           bottom: false ,           child: Builder(             builder: (BuildContext context) {               return  CustomScrollView(                  key: PageStorageKey<String >(name),                 slivers: <Widget>[                   SliverOverlapInjector(                      handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),                   ),                   SliverPadding(                     padding: const  EdgeInsets.all(8.0 ),                     sliver: SliverFixedExtentList(                        itemExtent: 48.0 ,                       delegate: SliverChildBuilderDelegate(                             (BuildContext context, int  index) {                           return  ListTile(                             title: Text('Item $index ' ),                           );                         },                         childCount: 30 ,                       ),                     ),                   ),                 ],               );             },           ),         );       }).toList(),     ),   ), )