Dissecting the Counter App

The 'Hello World' Flutter app is a simple counter that looks like this:

screen_shotscreen_shot

1. In your terminal run:

  1. flutter create my_app
  2. cd my_app

Open the project in your editor.

2. Now, run the app using your IDE or terminal.

In the terminal:

  1. flutter run

In IntelliJ: Across the top of the IDE there's a toolbar that lookssomething like this (although I have installed a theme so yours probably has different icons):

IntelliJ toolbar

  • Where mine says 'iPhone X' yours might be blank. If so, select 'Open iOS Simulator' in that drop down.
  • When it launches, click the 'play' button to run the app.NB: You can also click the 'debug' button to launch in Debug mode. I usually always just run it in debug mode while developing. It's the same thing but allows for breakpoints and the inspector.Once the app launches:

3. Click the '+' Button in the bottom-right of the app.

That's the only functionality. Watch the counter change.

What's happening? The button has an event listener of sorts that increases thevariable in your apps state that represents the counter. Flutter knows thatwhenever you change that state, it needs to repaint the widgets that rely onthat state — in this case the number.

4. Understand the Code

First, whats going on in the the directory structure:

  1. my_app
  2. |- android
  3. | ... a bunch of junk
  4. |- ios
  5. | ... a bunch of junk
  6. |- lib
  7. | main.dart
  8. |- test
  9. pubspec.lock
  10. pubspec.yaml
  11. README.md
  12. ...

99% of the time, you only care about the lib folder and the pubspec.yaml file, which is where you list your projects dependencies.

The lib folder is where you'll build your app. The only file here now is main.dart.

main.dart must exist and it must be in the root of lib. This is the file that Dart and Flutter know to run as the entry point to your app.

Let's see the code in the file:

  1. // import the Flutter sdk
  2. import 'package:flutter/material.dart';
  3. // Every Dart program ever must have a main() function
  4. // This function is essentially JavaScripts Document.ready(), only its required.
  5. // It's the entry point of your Dart code.
  6. // runApp is a Flutter function that runs the app.
  7. // It takes a Widget as an argument.
  8. void main() => runApp(new MyApp());
  9. // EVERYTHING is a Widget.
  10. // Including the root of your entire App:
  11. class MyApp extends Stateless Widget {
  12. ...
  13. // NB: MyApp is an arbitrary name.

Stateless and StatefulWidgets

Flutter widgets must extend a handful of classes from the Flutter library. Thetwo you'll use almost always are StatelessWidget and StatefulWidget. The difference is that one has a concept of state within the Widget and somebuilt in methods that tells Flutter to re-render if that state changes.

  1. // Stateless Widgets won't do anything to change state.
  2. // Root of your application
  3. class MyApp extends StatelessWidget {
  4. // Build method
  5. @override
  6. Widget build(BuildContext context) {
  7. // MaterialApp is a built in Flutter Widget that gives
  8. // us a lot styling out of the box.
  9. // The most important arguments for MaterialApp are title and home
  10. return new MaterialApp(
  11. // Arguments that Material App is looking for.
  12. title: 'Flutter Demo',
  13. theme: new ThemeData(
  14. // This is setting the primary color of the app to Blue.
  15. primarySwatch: Colors.blue,
  16. ),
  17. // MyHomePage is just another arbitrary widget
  18. // named by you, the developer
  19. home: new MyHomePage(title: 'Flutter Home Demo Page'),
  20. );
  21. }
  22. }
  23. // This Widget is Stateful,
  24. // because it's managing the state of the counter.
  25. class MyHomePage extends StatefulWidget {
  26. MyHomePage({Key key, this.title}) : super(key: key);
  27. // It's being passed in title, you can see above.
  28. final String title; // => Flutter Home Demo Page
  29. // Stateful Widgets don't have build methods.
  30. // They have createState() methods.
  31. // Create State returns a class that extends Flutters State class.
  32. @override
  33. _MyHomePageState createState() => new _MyHomePageState();
  34. // Stateful Widgets are rarely more complicated than this.
  35. }
  36. // This is the state that MyHomePage created.
  37. class _MyHomePageState extends State<MyHomePage> {
  38. int _counter = 0;
  39. // I've left in the comments from the sample app
  40. // because they say it better than I could:
  41. void _incrementCounter() {
  42. // Built in Flutter Method.
  43. setState(() {
  44. // This call to setState tells the Flutter framework that something has
  45. // changed in this State, which causes it to rerun the build method below
  46. // so that the display can reflect the updated values. If we changed
  47. // _counter without calling setState(), then the build method would not be
  48. // called again, and so nothing would appear to happen.
  49. _counter++;
  50. });
  51. }
  52. @override
  53. Widget build(BuildContext context) {
  54. // This method is rerun every time setState is called,
  55. // for instance as done by the _incrementCounter method above.
  56. // Scaffold is another build in app that gives us a standard
  57. // mobile app layout. You'll most likely use this on every page
  58. // of your app
  59. return new Scaffold(
  60. // the bar accross the top of the app
  61. appBar: new AppBar(
  62. // State classes access properties on their
  63. // parent by calling widget.someProperty
  64. // It's easier to think of StatefulWidgets and their corresponding
  65. // StateClasses as a single Widget.
  66. title: new Text(widget.title),
  67. ),
  68. body: new Center(
  69. // Center is a layout widget. It takes a single child
  70. // and positions it in the middle of the parent.
  71. child: new Column(
  72. // Column is also layout widget. It takes a List of children and
  73. // arranges them vertically. By default, it sizes itself to fit its
  74. // children horizontally, and tries to be as tall as its parent.
  75. //
  76. // Column has various properties to control how it sizes itself and
  77. // how it positions its children. Here we use mainAxisAlignment to
  78. // center the children vertically; the main axis here is the vertical
  79. // axis because Columns are vertical (the cross axis would be
  80. // horizontal).
  81. //
  82. // mainAxisAlignment and crossAxisAlignment should feel very familiar
  83. // if you're used to using CSS's Flexbox or Grid.
  84. mainAxisAlignment: MainAxisAlignment.center,
  85. children: <Widget>[
  86. new Text(
  87. 'You have pushed the button this many times:',
  88. ),
  89. new Text(
  90. // Text takes a String as it's first argument.
  91. // We're passing in the value of the counter
  92. // as an interpolated String.
  93. '$_counter',
  94. style: Theme.of(context).textTheme.display1,
  95. ),
  96. ],
  97. ),
  98. ),
  99. // Floating action buttons are special button
  100. floatingActionButton: new FloatingActionButton(
  101. onPressed: _incrementCounter,
  102. tooltip: 'Increment',
  103. child: new Icon(Icons.add),
  104. ),
  105. );
  106. }
  107. }

The Widget Tree

If all of this class-nesting is confusing, perhaps this visual of the Widget tree will help. This is what your current counter application looks like. You can think of nested widgets simply as nested React Components or HTML web components with all the JS functionality built in.

widget-tree