±à¼ÍƼö: |
±¾ÎÄÀ´×ÔÓÚ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¼ìË÷Óë×é¼þÁ¬½Ó״̬¡£
|