dwyl / learn-flutter

🦋 Learn how to use Flutter to Build Cross-platform Native Mobile Apps
https://flutter.dev
GNU General Public License v2.0
73 stars 8 forks source link

Smallest Flutter application #63

Open SimonLab opened 3 years ago

SimonLab commented 3 years ago

The small following example contains already multiple Flutter/Dart and OOP concepts. I'll try to explain them which hopefully will allow us to fully understand how this application works.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
 @override 
 Widget build(BuildContext context) {

   return Center( child: Text('hello', textDirection: TextDirection.ltr)
   );
 }
}
SimonLab commented 3 years ago

The import keyword allow us to use code from Dart built-in libraries (e.g core, math, async...), other people packages or from our own file code.

import 'dart:async`; // use dart: to import built-in libraries
import 'package:flutter/materials.dart'; // use package: to import packages via pub
import 'helpers/my_helpers.dart'; //user file path to reference other files in your project

The dart:core library is automatically imported in every Dart programs.

You can create a prefix name for your imports with the as keyword. This can be useful to differentiate methods with the same name but from different package.

import 'dart:async' as async; // To use a method from the async pakcage you can now prefix it with async.

You can specify what to import from a package with the show and hide keywords:

import 'dart:async' show Future; // only import the Future class
import 'dart:async' hide Future; // import all  from async except Future

You can use the deferred as keyword to import code only when needed.

import 'lib/my_functions.dart' deferred as my_functions;

You will then need to call the loadLibrary function to be able to access the method of the package.

Future func() async {
  await my_functions.loadLibrary(); //load the library
  my_functions.bonjour(); //call the bonjour method
}

See also the style guide when importing packages/files: https://dart.dev/guides/language/effective-dart/style#do-name-libraries-and-source-files-using-lowercase_with_underscores

references:

SimonLab commented 3 years ago

Similar to the main function which is the entry point of the dart program, the runApp function is the main entry point for your Flutter application. runApp takes a Widget as an argument which becomes the root widget of the application. It's possible to call runApp multiple times, however the last call will replace the previous widget tree and the last widget argument becomes the root widget:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp('hello'));
  return runApp(MyApp('hello there!!'));
}

class MyApp extends StatelessWidget {
  final String txt;

  MyApp(this.txt);

 @override 
 Widget build(BuildContext context) {

   return Center( child: Text(txt, textDirection: TextDirection.ltr)
   );
 }
}

In this example the class MyApp is a widget which takes a text to display on the screen as an argument. We can see that the main function calls MyApp twice but only the latest widget will remain and display the hello there text on the screen, the previous hello is discarded.

runApp doc: https://api.flutter.dev/flutter/widgets/runApp.html

SimonLab commented 3 years ago

A widget in Flutter is the configuration of some part of the UI. It describes how the UI element should be displayed on the screen. A widget is immutable, so the instance variable of the widget class are final (can be assigned only one time) and the constructor of the class is using const to force the creation of only one instance. The immutable annotation is also applied to the widget class:

@immutable
abstract class Widget extends DiagnosticableTree {
  /// Initializes [key] for subclasses.
  const Widget({ this.key });

Immutability is used with widgets to make it easier to determine which part of the UI needs to be updated. It is easier to build new widgets than to update them.

A stateless widget extends the widget class:

abstract class StatelessWidget extends Widget {

  const StatelessWidget({ Key key }) : super(key: key);

  @override
  StatelessElement createElement() => StatelessElement(this);

  @protected
  Widget build(BuildContext context);
}

The StatelessWidget is an abstract class which define the build interface (the body/logic of the method is left to the subclass to defined). In our example the MyApp class is forced to implement the build method and uses the override annotation:

 @override 
 Widget build(BuildContext context) {

   return Center( child: Text(txt, textDirection: TextDirection.ltr)
   );
 }

The build method describes the UI part of the widget. It has a BuildContext as parameter which describe where the widget is placed in the widget tree. One common use case of the context is to access other parent widgets.

The build function is also responsible for creating any child widgets. For example if the Column is used in the build any widgets in the children properties will also be built. This process is repeated until there aren't anymore widget to build in the widget tree. This process is used to create widget composition. See also this short flutter video on BuildContext: https://www.youtube.com/watch?v=rIaaH87z1-g

SimonLab commented 3 years ago
// import the material package
// add the package in pubspec.yaml file and use the pub get command line
import 'package:flutter/material.dart';

// void main() => runApp(MyApp());
// this arrow syntax is the same as
void main() {
  return runApp(MyApp()); //runApp returns void, so main has also void as returned type, https://api.flutter.dev/flutter/widgets/runApp.html
{

class MyApp extends StatelessWidget { // MyApp is a subclass of StatelesWidget
// the implementation of build is required on a stateless widget
// redefine the build  function inherited from StatelessWidget
 //the context describe where the MyApp widget is on the widget tree
 @override 
 Widget build(BuildContext context) {
    // the child is a Text Widget. MyApp widget is then composed of a child text widget
   return Center( child: Text('hello', textDirection: TextDirection.ltr)
   );
 }
}