The last post focused on the grammar of the Dart language. From this post, I will really be involved in the development of Flutter. I hope I can learn some new things as I write, and also help beginners with Flutter.
The index | The article |
---|---|
1 | Write an open Source Chinese Client based on Flutter from 0 (1) Flutter build | nuggets technical essay introduction and development environment |
2 | Write an open Source Chinese Client based on Flutter from 0 (2) Dart Grammar Basics |
👉 3 | Write an open Source Chinese Client based on Flutter from 0 (3) Introduction to Flutter & Common Widgets |
4 | Write an open Source Chinese Client based on Flutter from 0 (4) Foundation of Flutter layout |
5 | Write an open Source Chinese Client based on Flutter from 0 (5) Set up the overall layout framework of the App |
6 | Write an open Source Chinese Client based on Flutter from 0 (6) Implementation of various static pages |
7 | Write an open Source Chinese Client based on Flutter from 0 (7) App network requests and data stores |
8 | Write an open Source Chinese Client based on Flutter from 0 (8) Use of plug-ins |
One of the simplest Flutter apps
Create the project and add the code
Remember in the last post, we created the Flutter project using Android Studio? The newly created Flutter project automatically generates some code for us. The code is in the /lib/main.dart file. Here we empty the code in the /lib/main.dart file and replace it with the following code:
// main.dart File content import'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new Scaffold(
appBar: new AppBar(
title: new Text('First App')
),
body: new Center(
child: new Text('Hello world'),),),); }}Copy the code
Start simulator
In order to run our App on the phone, we first need to run an emulator (you can also debug it on the real phone). If your computer has the Flutter development environment configured correctly, everything is installed (and running)flutter doctor
The AndroidStudio Dart and Flutter plug-ins must also be installed), then you should see the following ICONS on the AndroidStudio toolbar:
Here, since I don’t have any Android/iOS emulators running on my computer, this is showing < No Devices >. Click this button and select Open iOS Simulator to start an iOS Simulator (make sure you have Xcode installed on your computer) as shown below:
If you want to create an Android emulator, you must first make sure you have an Android emulator available. Find the AVD Manager icon in the AndroidStudio toolbar, as shown below:
Click to open the Android Emulator Management dialog box, as shown below:
Create Virtual Devices...
Run the Dart code
In the first step we have written the code and in the second step our emulator is started. Click the Run button in the AndroidStudio toolbar to Run the Flutter project into our emulator. The Run button is in the position shown below:
As you can see, we’ve created a beautiful Demo App in just over 20 lines of code (which doesn’t do anything, but would it be that easy to do with Android or iOS native development), all thanks to the Widgets that Flutter provides us, The following section explains some of the common Widgets.
Structure of Flutter project
The structure of the newly created Flutter project is shown below:
The directories/files are described as follows:
..readme.md -- Markdown project description file...Android --Android source code directory...build -- Related file directory for output after the project build...flutter_app.iml Iml --Android related configuration files. ── ios -- ios source code directory. ── lib --Dart source code directory. ─ Pubspec.lock ├─ Pubspec. Yaml -- Flutter dependent configuration file, similar to build.grale ├─ test -- Test code directoryCopy the code
The code we developed is mainly stored in the lib/ directory, and the project entry file main.dart is also in the lib/ directory.
What is the Flutter App
There are a few things you need to know about a Flutter App:
- The layout file of the Flutter App is written using Dart code (both business logic code and UI code are written using Dart). There is no XML layout file like Android or XIB, storyboard files like iOS.
- The interfaces in the Flutter App are composed of widgets. Widgets come in two types: StatefulWidgets and StatelessWidgets. StatefulWidget represents a stateful component. When the state of the component changes, the component UI changes synchronously. StatelessWidget represents a stateless component that does not change its state and does not change its UI. If you are familiar with Reactjs, these two components in Flutter are easy to understand.
- Dart is a single-threaded language, which means that you don’t have to worry about threads synchronisation, locking, thread switching, etc. during Flutter development. Network requests, UI updates, etc., are all performed in one thread, but the time-consuming operations (network IO, File IO, etc.) will be put into the deferred operation queue so as not to block other operations and cause a lag.
- The biggest differences between Flutter and a mobile cross-platform framework like ReactNative or WEEX are: The Flutter uses AOT (Ahead of Time) or JIT (Just In Time) (JIT compilation In Debug mode and AOT compilation In Release mode) to directly compile Dart code into platform code for execution on mobile devices. ReactNative and WEEX have their own set of jsRender, which render JS code into native UI through the rendering engine. This process has the interoperation between JS and native, which is also a jsBridge. Therefore, although ReactNative or WEEX are also native applications written, But because of jsBridge, the code runs less efficiently than the Flutter App, which compiles directly into native code.
Flutter commonly used Widgets
In mobile development, we often deal with buttons, text input fields, pictures, etc., and the Flutter is no exception. In the App developed with Flutter, each UI element on the interface is a Widget, which is composed of different widgets to form an entire page. In addition to widgets such as buttons, input boxes, and pictures, Flutter also provides us with a number of powerful and beautiful widgets, such as the code at the beginning of this article:
// main.dart File content import'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new Scaffold(
appBar: new AppBar(
title: new Text('First App')
),
body: new Center(
child: new Text('Hello world'),),),); }}Copy the code
In the above code, MyApp is a custom class that inherits from StatelessWidget, meaning it is a stateless component and the UI does not change. The build method is a method of the parent class overridden by the MyApp class. Classes that inherit from StatelessWidget must implement the build method and return a Widget object. So in the code above, the MaterialApp is also a Widget. If you look at the source code with AndroidStudio, you can see that the home parameter of the MaterialApp is also a Widget object, so the Scaffold above is also a Widget.
StatefulWidget and StatelessWidget
Statefulwidgets and StatelessWidgets are two categories of all widgets in the Flutter. Statefulwidgets hold statuses internally, and when statuses change, the Widget interface changes (similar to React). No state is saved inside the StatelessWidget, and its interface does not change. The steps to define a StatelessWidget are shown in the code above: just inherit the StatelessWidget and implement the build method. If you were defining a stateful Widget, the code would be a little bit more, like this:
import 'package:flutter/material.dart'; void main() => runApp(new MyStatefulWidget()); Class MyStatefulWidget extends StatefulWidget {@override State<StatefulWidget>createState() {
returnnew MyStatefulWidgetState(); } // To define a stateful component, you must create a stateful class for that component. This class inherits from the State class Class MyStatefulWidgetState extends State<MyStatefulWidget> {String Text ="Click Me!";
changeText() {
if (text == "Click Me!") {
setState(() {
text = "Hello World!";
});
} else {
setState(() {
text = "Click Me!";
});
}
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: "Test",
home: new Scaffold(
appBar: new AppBar(
title: new Text("Test"InkWell is a Widget built into the Flutter that adds click events to other widgets and creates ripples when clicked child: new InkWell new Text(text), onTap: () { this.changeText(); },),),); }}Copy the code
When the above code runs, it will display text in the center of the page. When clicked, the text will appear in the “Click Me! And the “Hello World!” As shown in the figure below:
- The creation class inherits from the StatefulWidget and implements it
createState
Method, notice that this is different than the StatelessWidget, not the implementationbuild
Methods.createState
Method returns a State. - In order to make the first step
createState
Method has a return value, and you need to create a State class that inherits from the State class, which is a generic class, and you need to pass the class that you created in the first step to State. - After creating the custom State class, implement
build
Method and return the Widget you need. - In a custom State class, variables are used to hold the State of a component and change the State value when appropriate. For example, in the code above, we need to switch text when we click on it, so we use one
text
The variable holds the text value of the component by calling the State component’s when the button is clickedsetState()
Method, reset totext
Variable assignment to change the text.
If you know Reactjs, then this state mechanism of Flutter is no stranger. A state object is also used to store the Component state in React. When the state needs to change, the setState() method is called to change the state, and the Component automatically refreshes.
MaterialApp and Scaffold
The MaterialApp and Scaffold are two widgets that Flutter provides, of which:
- The MaterialApp is a handy Widget that encapsulates some of the widgets your application needs to implement Material Design. (reference)
- The Scaffold component is the basic implementation of the Material Design layout structure. This class provides apis for displaying drawers, Snackbars, and bottom sheets. (reference)
In my Open source Chinese client App based on Flutter, I also used the MaterialApp and Scaffold components. Here is some code:
@override
Widget build(BuildContext context) {
initData();
returnNew ThemealApp (Theme: new ThemeData(// Set theme Color: const Color(0xFF63CA6C)), home: New Scaffold(// set AppBar on top of App AppBar AppBar: new AppBar) New Text(appBarTitles[_tabIndex], // Style: new TextStyle(color: color.white)), // Icon on AppBar iconTheme: New icondata (color: color.white)), body: _body, bottomNavigationBar: new CupertinoTabBar(items: <BottomNavigationBarItem>[ new BottomNavigationBarItem( icon: getTabIcon(0), title: getTabTitle(0)), new BottomNavigationBarItem( icon: getTabIcon(1), title: getTabTitle(1)), new BottomNavigationBarItem( icon: getTabIcon(2), title: getTabTitle(2)), new BottomNavigationBarItem( icon: getTabIcon(3), title: getTabTitle(3)), ], currentIndex: _tabIndex, // Handle the bottom Tab click event onTap: (index) {setState((){ _tabIndex = index; }); },), // slide the menu where MyDrawer is a custom Widget drawer: new MyDrawer(),),); }Copy the code
Text component
The Text component is a very common component and can be used almost anywhere Text needs to be displayed. Text is a stateless component. The following code shows how to change the size, color, bold, underline, italic, and so on:
import 'package:flutter/material.dart';
void main() => runApp(new MaterialApp(
title: "Text Demo",
home: new Scaffold(
appBar: new AppBar(
title: new Text("Text Demo"),
),
body: new Center(
child: new Text(
"Hello Flutter", style: new TextStyle(color: color. red, // or write: const color (0xFF6699FF) 20.0, // font size: fontWeight. Bold, // font size: fontWeight. Bold, // fontStyle: fontStyle. New TextDecoration.combine([textdecoration.underline]),),),));Copy the code
Note:
- The Title parameter of the MaterialApp is a string type, while the title parameter of the AppBar is a Text component type.
- When the open source Chinese client based on Flutter was developed, Flutter was still in beta, which resulted in certain styles of Chinese text not working, such as bold, italic, etc. This issue appears to have been fixed in the current Flutter Preview release.
TextField components
The TextFiled component is used for text input. The sample code is as follows:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: "Test",
home: new Scaffold(
appBar: new AppBar(
title: new Text("Test"), body: New Padding(Padding: const edgeinset.all (8.0), Child: New TextField(maxLines: 8, maxLength: 30, // Set the maximum number of lines to be displayed (not the maximum number of lines to be entered), // Set the maximum number of characters to be entered (decoration: new InputDecoration(// set the style to hintText) :"Input something..."(placeholder text border: new OutlineInputBorder); Const BorderRadius. All (circular(1.0)))),)),); }}Copy the code
The running interface in the simulator is as follows:
The InkWell and GestureDetector
These two components are mentioned together because they are often used when dealing with a component’s click event. Click on a list item, click on an icon, and so on. The Flutter has buttons specifically designed in a MaterialDesign style, but more often than not we want to customize the button style or add a click event to a component, so the most common way to handle a click event is to wrap a component with InkWell or GestureDetector.
InkWell is used as follows:
new InkWell(
child: new Text("Click me!"), onTap: () {/ / click}, onDoubleTap: () {/ / double}, onLongPress: () {/ / long press});Copy the code
The use of GestureDetector is similar to that of InkWell, but GestureDetector has many more ways of handling gestures that I won’t cover here (I don’t actually use them much).
button
Flutter provides several types of button components: RaisedButton FloatingActionButton FlatButton IconButton PopupMenuButton. The following code shows how to use these buttons:
import 'package:flutter/material.dart';
main() {
runApp(new MyApp());
}
enum WhyFarther { harder, smarter, selfStarter, tradingCharter }
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Test',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Test')
),
body: new Column(
children: <Widget>[
new RaisedButton(
child: new Text("Raised Button"),
onPressed: (){},
),
new FloatingActionButton(
child: new Icon(Icons.add),
onPressed: (){},
),
new FlatButton(
onPressed: (){},
child: new Text("Flat Button")
),
new IconButton(
icon: new Icon(Icons.list),
onPressed: (){}
),
new PopupMenuButton<WhyFarther>(
onSelected: (WhyFarther result) {},
itemBuilder: (BuildContext context) => <PopupMenuEntry<WhyFarther>>[
const PopupMenuItem<WhyFarther>(
value: WhyFarther.harder,
child: const Text('Working a lot harder'),
),
const PopupMenuItem<WhyFarther>(
value: WhyFarther.smarter,
child: const Text('Being a lot smarter'),
),
const PopupMenuItem<WhyFarther>(
value: WhyFarther.selfStarter,
child: const Text('Being a self-starter'),
),
const PopupMenuItem<WhyFarther>(
value: WhyFarther.tradingCharter,
child: const Text('Placed in charge of trading charter'))))))))); }}Copy the code
In the emulator, the above code looks like the following:
Dialog component
Flutter provides two types of dialog boxes: SimpleDialog and AlertDialog. SimpleDialog is a SimpleDialog box that can display additional prompts or operations. AlertDialog is a dialog box that can interrupt user operations and needs user confirmation. The following code will explain its usage:
import 'package:flutter/material.dart';
main() {
runApp(new MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Test',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Test')
),
// body: new MyAlertDialogView()
body: new MySimpleDialogView(),
),
);
}
}
class MyAlertDialogView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new RaisedButton(
child: new Text('display AlertDialog'),
onPressed: () {
showDialog<Null>(
context: context,
barrierDismissible: false// Do not click outside the dialog to close the dialog box, you must click the button to close the Builder: (BuildContext Context) {return new AlertDialog(
title: new Text('tip'),
content: new Text('Microsoft has reiterated that Windows 7 will reach the end of support in January 2020, and the company hopes to use the opportunity to convince users to upgrade to Windows 10 before the latest update is released. '),
actions: <Widget>[
new FlatButton(
child: new Text('Got it'), onPressed: () { Navigator.of(context).pop(); },),,); }); }); } } class MySimpleDialogView extends StatelessWidget { @override Widget build(BuildContext context) {return new RaisedButton(
child: new Text('display SimpleDialog'),
onPressed: () {
showDialog(
context: context,
builder: (BuildContext ctx) {
return new SimpleDialog(
title: new Text('this is SimpleDialog'),
children: <Widget>[
new SimpleDialogOption(
onPressed: () { Navigator.pop(context); },
child: const Text('sure'),
),
new SimpleDialogOption(
onPressed: () { Navigator.pop(context); },
child: const Text('cancel'),),,); }); }); }}Copy the code
The code above shows the basic usage of SimpleDialog and AlertDialog, respectively. Note that you do not write the button and dialog logic directly to MyApp. Instead, you write two StatelessWidgets. If you write the button and dialog logic directly to MyApp’s build method, you will receive an error.
Navigator operation requested with a context that does not include a Navigator.
Copy the code
The navigation operation requires a context object that does not contain the Navigator, and if we write the showDialog logic to the build method of MyApp using the MaterialApp context object that contains the Navigator, we will report an error. The above code looks like this in the emulator:
Image components
The Image component is used to display an Image that can be loaded locally (in the project or in the phone’s storage) or on the network.
Loading local images
Load an image from a project using the following method:
New image.asset (path, width: 20.0, height: 20.0, fit: boxfit.cover)Copy the code
Where PATH is the image directory in the project.
When loading images in a project, be sure to edit the pubspec.yaml file:
Let’s assume that we have created an images/ directory in the same directory as lib/, and that we have several images in the images/ directory for the project. Make sure to edit the pubspec.yaml file in the root directory of the project (which is also the same directory as images/) and open the pubspec.yaml file. Assets are annotated by default, here we will unannotate assets and add the path to each image in the images/ directory, as shown below:
In the image above we configured the file path to images/ic_nav_news_normal.png, so we can load the image with the following code:
new Image.asset('images/ic_nav_news_normal.png', width: 20.0, height: 20.0, fit: BoxFit.cover)
Copy the code
Width and height are the width of the image, which is of type double. If you pass an integer of type 20, you will get an error. To load an image from your phone’s store, use the following method:
New image.file (path, width: 20.0, height: 20.0, fit: boxfit.cover)Copy the code
The fit attribute specifies the different ways the image can be displayed and has the following values:
- contain: As large as possible while still containing the image entirely within the target container.
- cover: as small as possible while still covering the entire target container.
- fill: Fills the target container by stretching the image by its aspect ratio.
- fitHeight: Ensures that the full height of the image is displayed, regardless of whether it overflows the target container.
- fitWidth: Ensures that the full width of the image is displayed, regardless of whether the image height overflows the target container.
- none: Aligns the image inside the target container (centered by default) and displaces any part of the image outside the container. The original image size will not be resized.
- scaleDown: Aligns the image in the target container (centered by default) and, if necessary, scales the image to make sure it fits the container. This has to do with
contain
Otherwise it is the same as none.
Loading web images
Load web images using the following method:
ImgUrl, width: 20.0, height: 20.0, fit: boxfit.cover)Copy the code
ListView component
The ListView component is used to display a list. In the Open source Chinese Client App Flutter based on the Flutter, ListView is used for news lists, listlists, etc. A simple ListView can be implemented with the following code:
import 'package:flutter/material.dart';
void main() {
List<Widget> items = new List();
for (var i = 0; i < 20; i++) {
items.add(new Text("List Item $i"));
}
runApp(new MaterialApp(
title: "Text Demo",
home: new Scaffold(
appBar: new AppBar(
title: new Text("Text Demo"),
),
body: new Center(
child: new ListView(children: items)
),
),
));
}
Copy the code
Run the above code, and the result looks like this:
This ListView display is not what we need, it’s ugly, each item has no margins and no dividing lines, so let’s transform it with the following code:
import 'package:flutter/material.dart';
void main() {// install a collection of all items in the ListView List<Widget> items = new List();for (var i = 0; i < 20; i++) {
var text = new Text("List Item $i"); Add (new Padding(// Padding is set to 15.0, top, bottom, left, right, and bottom are 15.0 Padding) : Const edgeinsets.all (15.0), // Padding container contains the Text component Child: Text)); } runApp(new MaterialApp( title:"Text Demo",
home: new Scaffold(
appBar: new AppBar(
title: new Text("Text Demo"),), body: new Center(// build is the static method provided by ListView to create ListView Child: New ListView.builder(itemCount = itemCount, itemCount = itemCount) new ListView.builder(itemCount = itemCount, itemCount = itemCount) Items. length * 2, itemBuilder: (context, index) {// Return the splitter line if index is oddif (index.isOdd) {
returnThe new Divider (height: 1.0); } index = index ~/ 2; index = index ~/ 2;returnitems[index]; },))),)); }Copy the code
Run the above code again at this point, and the UI looks much better:
The usage of the ListView is already commented in the above code, and will be described in more detail in a later section, such as implementing different layouts of items in the ListView, pull-down refreshes, loading more, and so on.
summary
Sample code and instructions for some of the widgets that are commonly used with Flutter are available above, and you can also check out more components and their usage on the Flutter Website. In the next post I will document the layout of the Flutter, which is an inevitable part of any mobile development, even Web development and desktop application development.
My open source project
- Google Flutter based on the open source Chinese client, hope you give a Star support, source code:
- GitHub
- Yards cloud
- Tetris based on the Flutter small game, I hope you give a Star support, source:
- GitHub
- Yards cloud
In the previous | The next article |
---|---|
Write an open Source Chinese Client based on Flutter from 0 (2) — Dart Syntax Basics |
Write an open Source Chinese Client based on Flutter from 0 (4) — Foundation of Flutter layout |