Äú¿ÉÒÔ¾èÖú£¬Ö§³ÖÎÒÃǵĹ«ÒæÊÂÒµ¡£

1Ôª 10Ôª 50Ôª





ÈÏÖ¤Â룺  ÑéÖ¤Âë,¿´²»Çå³þ?Çëµã»÷Ë¢ÐÂÑéÖ¤Âë ±ØÌî



  ÇóÖª ÎÄÕ ÎÄ¿â Lib ÊÓÆµ iPerson ¿Î³Ì ÈÏÖ¤ ×Éѯ ¹¤¾ß ½²×ù Model Center   Code  
»áÔ±   
   
 
     
   
 ¶©ÔÄ
  ¾èÖú
Flutter- ×é¼þ¿ò¼ÜÖ®ÂÃ
 
  1977  次浏览      27
2019-8-5
 
±à¼­ÍƼö:
±¾ÎÄÀ´×ÔÓÚjianshu£¬ÎÄÕ½éÉÜÁËFlutterµÄ¸÷¸ö×é¼þµÄ½ÓºÃÒÔ¼°Ê¹Óõȣ¬Ï£Íû¶ÔÄúÄÜÓÐËù°ïÖú¡£

ÒýÑÔ

Flutter×é¼þʹÓÃÏÖ´úµÄÏìӦʽ¿ò¼Ü(react-style framework)½¨Á¢£¬Áé¸ÐÀ´Ô´React¡£ºËÐÄ˼Ï룬ͨ¹ý×é¼þ(widget)¹¹½¨UI¡£Í¨¹ý¸ø×é¼þ(Widgets)ÉèÖÃËüÃǵ±Ç°µÄÅäÖÃ(configuration )ºÍ״̬(state)À´ÃèÊöËüÃÇ(Widgets)µÄ³¤Ïà¡£µ±×é¼þµÄ״̬·¢Éú¸Ä±ä£¬×é¼þÖØ½¨ËüµÄÃèÊö£¬ÎªÁËÈ·¶¨¹ý¶Èµ½ÏÂÒ»¸ö״̬ËùÐè×îС¸Ä±ä£¬¸ÃÃèÊöÊÇ¿ò¼Ü¶Ô±È֮ǰµÄÃèÊöµÈµ½µÄ²îÒì¡£

Ô­ÎÄ£ºWhen a widget¡¯s state changes, the widget rebuilds its description, which the framework diffs against the previous description in order to determine the minimal changes needed in the underlying render tree to transition from one state to the next.

Ó¢ÓïÌ«´Î£¬·­Ò벻׼ȷ£¬¸öÈËÀí½â´óÒ⣺¾ÍÊÇframeworkÈ¡µÃÁËǰºóÁ½¸ö״̬µÄ×îС¸Ä±ä£¬Ã»ÓбäµÄÊôÐÔ²»²Ù×÷£¬¸Ä±äÁ˵ÄËã²îÖµ½øÐиı䣬¶ø²»ÊÇÇå¿ÕÒ»¸ö״̬£¬ÔÙÉèÖÃÁíÒ»¸ö״̬¡£

 

Hello World

×îСµÄFlutterApp½ö½öͨ¹ý×é¼þµ÷ÓÃrunAppº¯Êý¡£

import 'package:flutter/material.dart';

void main() {
runApp(
new Center(
child: new Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}

runApp·½·¨»ñÈ¡µ½¸øËüµÄ×é¼þ( Widget)²¢°Ñ×é¼þ×÷ΪËüµÄ×é¼þÊ÷(widget tree)µÄ¸ù¡£

´ËÀýÖУ¬×é¼þÊ÷³ÖÓÐÁ½¸ö×é¼þ£¬ Center(¼Ì³ÐAlign) ºÍËüµÄ×Ó×é¼þ- Text ¡£¿ò¼ÜÇ¿ÖÆ¸ù×é¼þ(The root of the widget tree)ÆÌÂú(cover)ÆÁÄ»£¬ÕâÒâζ×ÅHello Worldtext×îÖÕλÓÚÆÁÄ»ÖÐÐÄ¡£´ËÀýÖеÄtextµÄ·½ÏòÐèÒªÖ¸¶¨¡£

The text direction needs to be specified in this instance; when the MaterialApp widget is used, this is taken care of for you, as demonstrated later.

µ±Ð´appʱ£¬Äãͨ³£»á´´½¨ StatelessWidget »ò StatefulWidgetµÄ×ÓÀà×÷Ϊ×é¼þ£¬¼Ì³ÐÄǸöÀàÈ¡¾öÓëÄãµÄ×é¼þÊÇ·ñ¹ÜÀí״̬state¡£Ò»¸ö×é¼þµÄÖ÷Òª¹¤×÷ʱʵÏÖbuild ·½·¨£¬Õâ¸ö·½·¨ÃèÊöÁËÕâ¸ö×é¼þÓëÆäËû×é¼þ»ò×Ó×é¼þµÄÌõÔ¼(terms)¡£¿ò¼Üframework»áÒÀ´Î´´½¨ÕâЩ×é¼þ£¬Ö±µ½³¬³ö×é¼þµÄµ×²¿£¬Õâ´ú±í¼ÆËãºÍÃèÊö×é¼þµÄ¼¸ºÎÐÎ×´µÄµ×²ãäÖȾ¶ÔÏóRenderObject.

The framework will build those widgets in turn until the process bottoms out in widgets that represent the underlying RenderObject, which computes and describes the geometry of the widget.

Basic Widgets

Main article: Widgets Overview - Layout Models

Flutter×Ô´øÒ»Ì×Ç¿´óµÄ»ù´¡×é¼þ(Basic widgets),ÒÔÏÂÊÇÆäÖÐһЩ³£ÓõÄ:Text:TextÓÃÓÚ´´½¨´øÑùʽµÄÎı¾

Row, Column: ÕâЩÁé»î(flex)µØ×é¼þ¿ÉÒÔÈÃÄãÔÚˮƽ(Row)ºÍ´¹Ö±(Column)·½Ïò´´½¨Áé»îµÄ²¼¾Ö¡£ËüµÄÉè¼ÆÊÇ»ùÓÚWebµÄflexbox²¼¾ÖÄ£ÐÍ

Stack: Stack×é¼þ¿ÉÒÔÈÃÄãµÄ×é¼þÔÚ»æÖÆË³ÐòÉϲã»ý(stack)Ôڱ˴˶¥²¿£¬¶ø²»ÊÇÏßÐÔ(ˮƽ»ò´¹Ö±)µÄ¡£¿ÉÒÔÔÚ×ÓStackÉÏʹÓà Positioned×é¼þÀ´·ÅÖÃËûÃǵ½Õâ¸öStackµÄtop,right,bottom»òleft±ß½ç¡£StackÉè¼ÆÊÇ»ùÓÚWebµÄabsolute positioning layout model(¾ø¶ÔλÖò¼¾ÖÄ£ÐÍ)¡£

Container: Container×é¼þ°ïÄã´´½¨Ò»¸ö¾ØÐÎÔªËØ¡£Ò»¸öContainer¿ÉÒÔ±»Ò»¸öBoxDecorationÐÞÊΣ¬Èç±³¾°(background)¡¢±ßÏß(border)¡¢ÒõÓ°(shadow)¡£Ò»¸öContainerÒ²¿ÉÒÔÓÐmargin,paddingºÍ¹Ì¶¨´óС¶¨Òå¡£ÁíÍ⣬Container¿ÉÒÔÓþØÕó(matrix)ÔÚÈýά¿Ø¼þ¸Ä±ä¡£

ÒÔÏÂÊÇһЩ×éºÏÕâЩ×é¼þµÄÀý×Ó£º

import 'package:flutter/material.dart';

class MyAppBar extends StatelessWidget {
MyAppBar({this.title});

// Fields in a Widget subclass are always marked "final".

final Widget title;

@override
Widget build(BuildContext context) {
return new Container(
height: 56.0, // in logical pixels
padding: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: new BoxDecoration(color: Colors.blue[500]),
// Row is a horizontal, linear layout.
child: new Row(
// <Widget> is the type of items in the list.
children: <Widget>[
new IconButton(
icon: new Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null, // null disables the button
),
// Expanded expands its child to fill the available space.
new Expanded(
child: title,
),
new IconButton(
icon: new Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
);
}
}

class MyScaffold extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Material is a conceptual piece of paper on which the UI appears.
return new Material(
// Column is a vertical, linear layout.
child: new Column(
children: <Widget>[
new MyAppBar(
title: new Text(
'Example title',
style: Theme.of(context).primaryTextTheme.title,
),
),
new Expanded(
child: new Center(
child: new Text('Hello, world!'),
),
),
],
),
);
}
}

void main() {
runApp(new MaterialApp(
title: 'My app', // used by the OS task switcher
home: new MyScaffold(),
));
}

È·±£ÔÚpubspec.yamlÎļþµÄ flutter Ò»½ÚÏÂÓÐ uses-material-design: true ÉèÖã¬ËüÔÊÐíʹÓÃÔ¤¶¨ÒåµÄMaterial icons¼¯ºÏ¡£

name: my_app
flutter:
uses-material-design: true

ÔËÐÐЧ¹û

Ðí¶à×é¼þÐèÒªÔÚ MaterialAppÄÚ²¿²ÅÕýÈ·ÏÔʾ£¬¼Ì³ÐËûÃǵÄÖ÷ÌâÊý¾Ý(Theme data),Òò´ËÎÒÃÇÔËÐÐÒ»¸öMaterialApp³ÌÐò¡£

MyAppBar×é¼þ´´½¨Ò»¸ö¸ß56dip( device-independent pixels)¼°ÄÚ²¿padding 8px,´Ó×óµ½ÓÒµÄContainer×é¼þ¡£ÔÚContainerÖУ¬MyAppBarʹÓÃRow²¼¾Ö(layout)À´¹ÜÀíËüµÄ×ӿؼþ¡£Öмä¶ù×Ó£¬title×é¼þ£¬±ê¼ÇΪExpanded,ÒâΪËü¿ÉÒÔÀ©Õ¹Ìî³äÈκÎÊ£ÓàµÄ¡¢Î´±»ÆäËû×ӿؼþÕ¼ÓõĿռ䡣Äã¿ÉÄÜÓжàÖØExpanded×ӿؼþ£¬Ê¹ÓÃflexÀ´È·¶¨ËûÃǸ÷×ÔÕ¼ÓÿÉÓÿռäµÄ±ÈÀý(You can have multiple Expanded children and determine the ratio in which they consume the available space using the flex argument to Expanded)¡£

MyScaffold×é¼þÔÚ´¹Ö±Áз½Ïò(vertical column)¹ÜÀíËüµÄ×ӿؼþ¡£ÔÚÁж¥£¬Ëü·ÅÁËÒ»¸öMyAppBarʵÀý£¬´«µÝÒ»¸öText×é¼þ×öapp barµÄtitle¡£´«µÝ×é¼þ(Passing widgets)×÷ΪÁíÒ»¸ö×齨µÄ²ÎÊýÊÇÒ»¸öÇ¿´óµÄ¼¼Êõ£¬ËüÔÊÐíÄã´´½¨µÄ³£ÓÃ×é¼þ¶àÑùÖØÓá£×îºó£¬¾ÓÖÐÏÔʾÐÅÐĵÄMyScaffoldʹÓÃExpandedÀ´Ìî³äÊ£ÓàµÄ¿Õ¼ä¡£

Using Material Components

Main article: Widgets Overview - Material Components

FlutterÌṩÁËÐí¶à×ñÑ­Material DesignµÄ×é¼þ°ïÖúÄã´´½¨app¡£Ò»¸öMaterial appʼÓÚMaterialApp×é¼þ£¬MaterialApp×é¼þ×÷ΪÄãappµÄ¸ù(root)´´½¨Ðí¶àÓÐÓõÄ×é¼þ£¬°üÀ¨ ¹ÜÀíÒ»¶ÑʹÓÃstringsÇø·ÖµÄ×é¼þ¡¢Òà±»³ÆÎªroutesµÄNavigator¡£Navigator ÈÃÄãÆ½»¬ÔÚappµÄscreens¼äÇл»¡£Ê¹ÓÃMaterialApp²»ÊDZØÐëµÄ£¬µ«ÊÇÊÇÒ»¸öºÜºÃµÄ¹ßÀý¡£

import 'package:flutter/material.dart';

void main() {
runApp(new MaterialApp(
title: 'Flutter Tutorial',
home: new TutorialHome(),
));
}

class TutorialHome extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Scaffold is a layout for the major Material Components.
return new Scaffold(
appBar: new AppBar(
leading: new IconButton(
icon: new Icon(Icons.menu),
tooltip: 'Navigation menu',
onPressed: null,
),
title: new Text('Example title'),
actions: <Widget>[
new IconButton(
icon: new Icon(Icons.search),
tooltip: 'Search',
onPressed: null,
),
],
),
// body is the majority of the screen.
body: new Center(
child: new Text('Hello, world!'),
),
floatingActionButton: new FloatingActionButton(
tooltip: 'Add', // used by assistive technologies
child: new Icon(Icons.add),
onPressed: null,
),
);
}
}

ÔËÐÐЧ¹û

ÏÖÔÚÎÒÃÇÌæ»»MyAppBar and MyScaffoldΪÀ´×Ômaterial.dartµÄAppBar ºÍScaffold £¬ÎÒÃǵÄapp¿´ÆðÀ´¸ü¼ÓMaterial¡£ÀýÈ磬app barÓÐÒõÓ°ÁË£¬titleÎı¾×Ô¶¯¼Ì³ÐÁËÕýÈ·µÄÑùʽ¡£ÎÒÃÇÒ²Ìí¼ÓÁËÒ»¸öºÏÊʵĸ¡¶¯°´Å¥(FloatingActionButton)¡£

×¢Ò⣬ÎÒÃÇÔٴΰÑ×é¼þ×÷Ϊ²ÎÊý´«µÝ¸øÁíÒ»¸ö×é¼þ¡£ScaffoldÐèÒªÐí¶à²»Í¬µÄ×é¼þ×÷Ϊ²ÎÊý£¬ËûÃǽ«±»·ÅÔÚScaffoldµÄÊʵ±Î»Öá£ÀàËÆµÄ£¬AppBar×é¼þÈÃÎÒÃÇ´«µÝ×é¼þ×÷Ϊ title×é¼þµÄleading ºÍ actions ¡£ÕâÖÖģʽ±é²¼Õû¸ö¿ò¼Ü£¬ÄãÔÚÉè¼Æ×Ô¼ºµÄ×é¼þʱҲÐèÒª¿¼ÂÇ¡£

Handling gestures ´¦ÀíÊÖÊÆ

Main article: Gestures in Flutter

´ó²¿·Öapp°üº¬Ò»Ð©Óû§Óëϵͳ½»»¥µÄ±íµ¥¡£µÚÒ»²½¾ÍÊÇÒª´´½¨Ò»¸ö¿É½»»¥µÄappÈ¥¼ì²âÊäÈëµÄÊÖÊÆgetsures¡£´´½¨Ò»¸öʵÀý°´Å¥À´¿´¿´ÕâÊÇÈçºÎ¹¤×÷µÄ£º

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'),
),
),
);
}
}

GestureDetector ×é¼þ²»¿É¼û£¬µ«¿ÉÒÔ¼ì²âÓû§ÊÖÊÆgestures¡£µ±Óû§Çá»÷Container£¬GestureDetector½«»Øµ÷ËüµÄonTap·½·¨£¬Õâ¸öÀý×ÓÖУ¬ÔÚ¿ØÖÆÌ¨console´òÓ¡ÁËÒ»ÌõÏûÏ¢¡£Äã¿ÉÒÔʹÓÃGestureDetectorÀ´¼ì²âһϵÁÐÊäÈëÊÖÊÆ£¬°üÀ¨µã»÷tap,Í϶¯dragºÍËõ·Åscale¡£

Ðí¶à×é¼þʹÓÃGestureDetectorÀ´ÓÃÓÚÆäËû×é¼þµÄ¿ÉÑ¡»Øµ÷¡£ÀýÈ磬IconButton,RaisedButton, FloatingActionButton ÓÐÒ»¸ö onPressed »Øµ÷·½·¨£¬µ±Óû§µã»÷ÕâЩ×é¼þʱ´¥·¢¡£

Changing widgets in response to input

Main articles: StatefulWidget, State.setState

Ŀǰ£¬ÎÒÃǽöʹÓÃÁËstateless×é¼þ¡£Stateless widgets´ÓËûÃǵĸ¸Àà½ÓÊܲÎÊý£¬ËûÃDZ£´æÁË final ³ÉÔ±±äÁ¿¡£µ±Ò»¸ö×é¼þµ÷ÓÃbuildʱ£¬ËüʹÓÃÕâЩÒÑ´æµÄÖµÌáȡвÎÊý(derive new arguments )À´´´½¨×é¼þ¡£

ΪÁË´´½¨¸ü¼Ó¸´ÔÓµÄÌåÑ飬ÀýÈ磬ÒÔ¸üÓÐȤµÄ·½Ê½ÏìÓ¦Óû§µÄÊäÈ룬appͨ³£´øÓÐijЩ״̬¡£FlutterʹÓÃStatefulWidgets×é¼þ»ñÈ¡ÕâЩidea.StatefulWidgetsÊÇÖªµÀÈçºÎ´´½¨State¶ÔÏóµÄÌØÊâ×é¼þ£¬State³ÖÓÐstate¡£ÔÚ´ËÀýÖУ¬Ê¹ÓÃRaisedButton mentioned earlier£º

class Counter extends StatefulWidget {
// This class is the configuration for the state. It holds the
// values (in this nothing) provided by the parent and used by the build
// method of the State. Fields in a Widget subclass are always marked "final".

@override
_CounterState createState() => new _CounterState();
}

class _CounterState extends State<Counter> {
int _counter = 0;

void _increment() {
setState(() {
// This call to setState tells the Flutter framework that
// something has changed in this State, which causes it to rerun
// the build method below so that the display can reflect the
// updated values. If we changed _counter without calling
// setState(), then the build method would not be called again,
// and so nothing would appear to happen.
_counter++;
});
}

@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance
// as done by the _increment method above.
// The Flutter framework has been optimized to make rerunning
// build methods fast, so that you can just rebuild anything that
// needs updating rather than having to individually change
// instances of widgets.
return new Row(
children: <Widget>[
new RaisedButton(
onPressed: _increment,
child: new Text('Increment'),
),
new Text('Count: $_counter'),
],
);
}
}

ÔËÐÐЧ¹û

Äã¿ÉÄÜºÃÆæ£¬ÎªÊ²Ã´StatefulWidgetºÍStateÊÇ·ÖÀëµÄ¶ÔÏó¡£ÔÚFlutterÖУ¬ÕâÁ½Àà¶ÔÏóÓв»Í¬µÄÉúÃüÖÜÆÚ¡£WidgetsÊÇÁÙʱ¶ÔÏó£¬ÓÃÓÚ¹¹½¨appµ±Ç°×´Ì¬µÄ±í´ï(presentation)¡£ÁíÒ»·½Ã棬StateÊÇÔÚbuild()Ö®¼äÊdzÖÐøµÄ£¬ÔÊÐíËûÃǼÇÒäÐÅÏ¢¡£

ÉÏÀýÖУ¬½ÓÊÕÓû§ÊäÈëºÍÒ²½ÓÊÜËüµÄbuild·½·¨ÖеĽá¹û¡£ÔÚ¸ü¸´ÔÓµÄappÖУ¬²»Í¬²ã¼¶µÄ×é¼þ¿ÉÄܸºÔð²»Í¬¹Ø×¢µã¡£ÀýÈ磬һ¸ö×é¼þ¿ÉÄܳÊÏÖ¸´ÔÓµÄÓû§½çÃæ£¬ÆäÄ¿µÄÊÇÊÕ¼¯Ìض¨ÐÅÏ¢£¬ÈçÐÅÏ¢»òλÖ㬶øÁíÒ»¸ö×é¼þ¿ÉÄÜʹÓÃÕâЩÐÅÏ¢¸ü¸Ä×ÜÌå±íÏÖ¡£

ÔÚFlutterÖУ¬¸ü¸ÄÐÅÏ¢Á÷ÒÀÀµ»Øµ÷×é¼þ²ã´Î½á¹¹£¬µ±µ±Ç°×´Ì¬Á÷Ïòstateless×é¼þ¡£State¾ÍÊǸ¸ÀàÖØ¶¨ÏòÕâЩÐÅÏ¢Á÷¡£ÎÒÃÇÀ´¿´¿´Êµ¼ÊÖÐÊÇÈçºÎ¹¤×÷µÄ£¬ÕâÊÇÒ»¸öÉÔ΢¸´ÔÓµÄÀý×Ó£º

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),
]);
}
}

Çë×¢ÒâÎÒÃÇÈçºÎ´´½¨2¸östateless×é¼þ£¬ÇåÎú·ÖÀëÁ˹Ø×¢µã£ºÏÔʾdisplaying¼ÆÊýÆ÷(CounterDisplay)ºÍ¸Ä±ächanging¼ÆÊýÆ÷(CounterIncrementor)¡£ËäÈ»×îÖÕ½á¹ûºÍ֮ǰһÑù£¬µ«·ÑÁ¦ÔðÈÎÔÊÐíÔÚµ¥¸ö×é¼þÖмÓÈë¸ü¶à¸´ÔÓÐÔ£¬Í¬Ê±±£³Ö¸¸¼¶µÄ¼òµ¥ÐÔ¡£

Bringing it all together

ÎÒÃÇÀ´Ë¼¿¼Ò»¸ö¸ü¸´ÔÓµÄÀý×Ó£¬°ÑÒÔÉϹ۵㶼»ã¾ÛÔÚÒ»Æð¡£ÎÒÃÇÀ´¼ÙÉèÒ»¸ö¹ºÎïapp,չʾ¸÷ÖÖ´ýÊÛ²úÆ·£¬²¢Î¬»¤Ò»¸ö¹ºÎï³µÓÃÓÚ¹ºÂò¡£ÎÒÃÇÏÈÀ´¶¨Òå presentation class-ShoppingListItem:

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) {
// The theme depends on the BuildContext because different parts of the tree
// can have different themes. The BuildContext indicates where the build is
// taking place and therefore which theme to use.

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¼Ì³Ð×ÔÒ»¸östateless ×é¼þµÄͨÓÃģʽ¡£Ëü±£´æÀ´×ÔËüµÄ¹¹Ô캯Êý½ÓÊܵÄÖµ¸øËüµÄ final ³ÉÔ±±äÁ¿£¬ÕâЩֵÔÚËüµÄbuild·½·¨ÖÐʹÓá£ÀýÈ磬inCartÇл»Á½ÖÖ¿ÉÊÓÍâ¹Û£¬Ò»¸öʹÓõ±Ç°Ö÷ÌâµÄprimaryÑÕÉ«£¬ÁíÒ»¸öʹÓûÒÉ«gray¡£

µ±Óû§µã»÷ÌõÄ¿£¬×é¼þ²»Ö±½Ó¸Ä±äËüµÄinCartµÄÖµ¡£¶øÊǵ÷Óø¸ÀàµÄonCartChanged·½·¨¡£ÕâÖÖģʽÈÃÄã±£´æ×´Ì¬stateµ½¸ü¸ßµÄ×é¼þ²ã¼¶£¬ÕâÊÇ״̬³ÖÐø¸ü³¤Ê±ÆÚµÄÔ­Òò¡£Õâ¸öÀý×ÓÖУ¬±£´æÔÚ×é¼þÖеÄ״̬stateͨ¹ýrunApp³ÖÐø´æÔÚÓÚappµÄÉúÃüÖÜÆÚÖС£

µ±¸¸ÀàÊÕµ½onCartChanged»Øµ÷£¬¸¸Àཫ¸üÐÂËüµÄÄÚ²¿×´Ì¬state£¬Õ⽫Òý·¢¸¸ÀàÖØ½¨²¢Ð½¨Ò»¸ö´øÓÐÐÂinCartÖµµÄеÄShopingListItemʵÀý¡£ËäÈ»¸¸ÀàÖØ½¨Ê±´´½¨Ò»¸öеÄShoppingListItemʵÀý£¬µ«ÊÇÕâ¸ö²Ù×÷ÊÇÁ®¼ÛµÄ£¬ÒòΪ¿ò¼Ü¶Ô±ÈÁËеÄ×é¼þºÍ¾ÉµÄ×é¼þ£¬Ö»Ó¦Óò»Í¬µÄRenderObject¡£

À´¿´Ò»¸ö±£´æ¿É±ä״̬µÄ¸¸Àࣺ

class ShoppingList extends StatefulWidget {
ShoppingList({Key key, this.products}) : super(key: key);

final List<Product> products;

// The framework calls createState the first time a widget appears at a given
// location in the tree. If the parent rebuilds and uses the same type of
// widget (with the same key), the framework will re-use the State object
// instead of creating a new State object.

@override
_ShoppingListState createState() => new _ShoppingListState();
}

class _ShoppingListState extends State<ShoppingList> {
Set<Product> _shoppingCart = new Set<Product>();

void _handleCartChanged(Product product, bool inCart) {
setState(() {
// When user changes what is in the cart, we need to change _shoppingCart
// inside a setState call to trigger a rebuild. The framework then calls
// build, below, which updates the visual appearance of the app.

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'),
],
),
));
}

ÔËÐÐЧ¹û

ShoppingListÀà¼Ì³Ð×Ô ±£´æ¿É±ä״̬µÄ StatefulWidget¡£µ±ShoppingList×é¼þÊ״βåÈëÊ÷(tree)ÖУ¬¿ò¼Üµ÷ÓÃcreateState·½·¨À´´´½¨Ò»¸ö×îеÄ_ShoppingListStateʵÀýÀ´Á¬½ÓÊ÷£¨tree£©¡£(×¢ÒâÎÒÃÇͨ³£Ê¹ÓÃ_¿ªÍ·À´¶¨ÒåStateµÄ×ÓÀàÀ´±íʾËûÃÇ˽ÓÐʵÏÖÏêÇé(private implementation details)) µ±×é¼þµÄ¸¸ÀàÖØ½¨£¬¸¸Àà»á´´½¨Ò»¸öеÄShoppingListʵÀý£¬µ«ÊÇ¿ò¼Ü»áÖØÓÃÒѾ­´æÔÚÊ÷£¨tree£©ÖеÄ_ShoppingListStateʵÀý£¬¶ø²»ÊÇÔٴε÷ÓÃcreateState¡£

ΪÁË»ñÈ¡µ±Ç°ShoppingListÌØÐÔ£¨properties £©£¬_ShoppingListState»áʹÓÃËü×ÔÉíwidgetÌØÐÔ¡£Èç¹û¸¸ÀàÖØ½¨²¢´´½¨Ò»¸öеÄShoppingList,_ShoppingListStateÒ²»áÖØÐ´´½¨Ò»¸öеÄwidgetÖµ¡£Èç¹ûÄãÏ£Íûµ± widget ÌØÐԸıäʱµÃµ½Í¨Öª£¬¿ÉÒÔ¸´Ð´didUpdateWidget·½·¨£¬Ëü»á´«µÝoldWidget,Äã¿ÉÒÔÓ뵱ǰwidget½øÐжԱȡ£

µ±´¦ÀíonCartChanged»Øµ÷ʱ£¬_ShoppingListStateͨ¹ý¸ø_shoppingCartÔö¼Ó»òɾ³ýÒ»¸öproduct¸Ä±äËüÄÚ²¿×´Ì¬¡£Í¨¹ýµ÷ÓÃsetState ֪ͨ¿ò¼Ü£¨framework£©ËüÄÚ²¿×´Ì¬£¨state£©µÄ¸Ä±ä¡£µ÷ÓÃsetState±ê¼ÇwidgetΪÔàdirty²¢°²ÅÅËüÔÚÏ´θüÐÂÆÁÄ»Ê±ÖØ½¨¡£Èç¹ûÄÚ²¿×´Ì¬¸Ä±äʱÄãÍü¼Çµ÷ÓÃsetState£¬¿ò¼Ü(Framework)²»»áÖªµÀÄãµÄwidgetÊÇÔàµÄ(dirty)£¬Ò²¾Í²»»áµ÷ÓÃwidgetµÄbuild·½·¨£¬ÕâÒâζ×Å£¬Óû§½çÃæ²»»á¸üз´Ó¦×´Ì¬µÄ¸Ä±ä¡£

ͨ¹ýÕâÖÖ·½Ê½¹ÜÀístate,Äã²»±Ø·Ö±ðΪ´´½¨ºÍ¸üÐÂ×Ówidgetд´úÂ룬ֻÐè¼òµ¥µÄʵÏÖbuild·½·¨£¬Ëü»á´¦ÀíÁ½ÖÖÇé¿ö¡£

Responding to widget lifecycle events ×é¼þÉúÃüÖÜÆÚÏìÓ¦

Main article: State

StatefulWidgetµ÷Óà createStateÖ®ºó£¬¿ò¼Ü(framework)²åÈëÒ»¸öеÄstate¶ÔÏóµ½Ê÷(tree)ÖУ¬È»ºóµ÷ÓÃstate¶ÔÏóµÄ initState¡£StateµÄ×ÓÀà¿ÉÒÔ¸´Ð´(override)initStateÀ´×öÖ»ÐèÖ´ÐÐÒ»´ÎµÄ¹¤×÷¡£ÀýÈ磬Äã¿ÉÒÔ¸´Ð´(override)initStateÀ´ÅäÖö¯»­»ò¶©ÔÄÆ½Ì¨·þÎñ(subscribe to platform services)¡£initStateµÄʵÏÖÒªÇóÒÔµ÷ÓÃsuper.initState¿ªÊ¼¡£

µ±Ò»¸östate¶ÔÏó²»ÔÚ±»ÐèÒª£¬¿ò¼Ü(framework)µ÷ÓÃstate¶ÔÏóµÄdispose ¡£Äã¿ÉÒÔ¸´Ð´(override)dispose·½·¨À´ÇåÀí¹¤×÷¡£ÀýÈ磬ͨ¹ý¸´Ð´disposeÀ´È¡Ïû¼ÆÊ±Æ÷»ò½â³ýƽ̨·þÎñ¶©ÔÄ¡£disposeʵÏÖͨ³£ÒÔµ÷ÓÃsuper.dispose½áÊø¡£

Keys

Main article: Key

Äã¿ÉÒÔʹÓÃkeysÀ´¿ØÖƵ±Ò»¸ö×é¼þÖØ½¨Ê±£¬¿ò¼Ü½«Æ¥ÅäÄÇЩÆäËû×é¼þ¡£Ä¬Èϵģ¬¿ò¼Ü»á¸ù¾ÝËûÃÇµÄ runtimeTypeºÍËûÃdzöÏÖµÄ˳ÐòÆ¥Å䵱ǰºÍǰһ¸ö×é¼þ¡£´øÓÐkeysµÄ£¬¿ò¼Ü£¨framework£©»áÒªÇó2¸ö×é¼þÓÐÏàͬµÄkeyºÍÏàͬµÄruntimeType¡£

µ±´´½¨ºÜ¶àÏàͬÀàÐÍ×é¼þµÄʵÀýʱKeysÊǷdz£ÓÐÓõġ£ÀýÈç,ÉÏÀýÖÐShoppingList×é¼þ£¬´´½¨ÁË×ã¹»¶àµÄShoppingListItemʵÀýÀ´Ìî³äËüµÄ¿ÉÊÓÇøÓò:

ûÓÐkeys,µ±Ç°´´½¨µÄµÚÒ»¸öentry»áÒ»Ö±Óëǰһ´Î´´½¨µÄµÚÒ»¸öentryͬ²½£¬¼´Ê¹ÁбíÖеĵÚÒ»¸öentry¹ö³öÆÁÄ»¶øÇÒ²»Ôٿɼû¡£

ͨ¹ýÖ¸ÅÉÁбíÖÐÿһ¸öentryÒ»¸ösemantic* key,ÎÞÏÞÁбí¿ÉÒÔ¸üÓÐЧ£¬ÒòΪ¿ò¼Ü»áͬ²½entries Æ¥Åäsemantic keysºÍÒò´ËÏàͬ»ò½üËÆµÄÍâ¹Û¡£´ËÍ⣬ͬ²½entriesÓïÒåsemanticallyÒâζ×ÅÔÚstatefulµÄ×Ó×é¼þÖеÄstate»á±£³ÖÁ¬½Óµ½Ïàͬsemantic entry£¬¶ø²»ÊÇÁ¬½Óµ½ÔÚviewportÖеÄÏàͬÊýֵλÖõÄentry¡£

Global Keys È«¾ÖKeys

Main article: GlobalKey

Äã¿ÉÒÔʹÓÃÈ«¾ÖkeysΨһµÄ±êʶ×Ó×é¼þ¡£È«¾Ökeys±ØÐëÔÚÕû¸ö×é¼þ²ã¼¶ÊÇÈ«¾ÖΨһµÄ£¬²»Ïñ±¾µØlocal keyÖ»ÐèÔÚÐÖµÜÖ®¼äΨһ¡£ÒòΪËûÃÇÈ«¾ÖΨһ£¬¿ÉÒÔÓÃÈ«¾Ökey¼ìË÷Óë×é¼þÁ¬½Ó״̬¡£

 
   
1977 ´Îä¯ÀÀ       27
Ïà¹ØÎÄÕÂ

Éî¶È½âÎö£ºÇåÀíÀôúÂë
ÈçºÎ±àд³öÓµ±§±ä»¯µÄ´úÂë
ÖØ¹¹-ʹ´úÂë¸ü¼ò½àÓÅÃÀ
ÍŶÓÏîÄ¿¿ª·¢"±àÂë¹æ·¶"ϵÁÐÎÄÕÂ
Ïà¹ØÎĵµ

ÖØ¹¹-¸ÄÉÆ¼ÈÓдúÂëµÄÉè¼Æ
Èí¼þÖØ¹¹v2
´úÂëÕû½àÖ®µÀ
¸ßÖÊÁ¿±à³Ì¹æ·¶
Ïà¹Ø¿Î³Ì

»ùÓÚHTML5¿Í»§¶Ë¡¢Web¶ËµÄÓ¦Óÿª·¢
HTML 5+CSS ¿ª·¢
ǶÈëʽC¸ßÖÊÁ¿±à³Ì
C++¸ß¼¶±à³Ì