Welcome to Tikal's TechRadar Flutter Workshop

Flutter is a great framework for creating beautiful apps both for Android and IOS, and in the future, for more platforms.

The apps are based on a single codebase which developed in Google's Dart programming language.

Flutter Key Features:

Flutter Home Page

Dart Home Page

What you will build:

In this workshop - codelab, you will learn to install and setup Flutter environment, create your first app, learn the basic concepts of Flutter such as Widget, Context and State

Your app will:

What you will learn:

Get the complete Source Code Here

Download Android Studio For Mac Download Android Studio Linux

Download Android Studio Windows

  1. Download Android Studio installation package, use the above links
  2. Follow Android Studio wizard to complete Android Studio installation
  3. When Installation completes, open Android Studio
  4. In the welcome screen, select Plugin from the Configure bottom right menu:

  1. Install the Flutter and Dart plugins:
  2. Restart Android Studio to apply plugins changes
  3. Close Android Studio

Download Flutter - MAC Download Flutter - Linux Download Flutter - Window

  1. Download the Flutter SDK package for you platform, use the links above
  2. Extract the package into a selected folder, e.g: $HOME/development/Flutter
  3. Add the Flutter/bin path to your system $PATH env' variable

On a Mac, open your ~/.bash.profile and add the following line:

PATH=$PATH:$HOME/Development/flutter/bin/cache/dart-sdk/bin:
$HOME/Library/Android/sdk/platform-tools:
$HOME/Development/flutter/bin
  1. Once your $PATH is updated, open a new Terminal and write the following command: flutter doctor
  1. Follow Flutter doctor instructions to complete the setup until you get [✓] in the Android toolchain and Android studio
  2. Accept Android SDK licenses if required
  1. Proceed to next step

What you will do here:

Create New Flutter Project

  1. Open Android Studio
  2. In the welcome Screen Select: Create new Flutter Project
  3. In the next screen select Flutter Application
  4. Set project name and location
  5. Next, enable Kotlin and Swift Support
  6. Complete the project initialization

Explore Flutter Project Structure

Create an Android Emulator

  1. Click on the AVD manager icon:
  2. Click on +Create Virtual Device button

  1. Select the default emulator and click Next
  2. Select a System Image, download it if required (Recommended for this tutorial, 27 - Oreo)

  1. Continue to configuration, set the emulator name,
  2. Click Finish.
  3. Click the run button to start the emulator
  4. Once emulator is running, run flutter doctor command again, notice device connected.

Run the default app

  1. Run the app from the Run icon
  2. This is the Flutter default demonstration app
  3. Check out the counter button operation
  4. In the project file, under the MaterialApp modify the theme color from blue to green
  5. Press Ctrl+S to save, this will do Hot Reload, notice the color change on the screen.

Run the App from the command line

  1. Stop the app from the Stop button
  2. Open IDE terminal
  3. Write command flutter run -d all
  4. Wait for the app to run
  5. Change the _counter++ to decrement _counter-- (on line 59)
  6. In the terminal, press r to do hot reload, press the + button, the counter should now decrement.

What you will do here:

Get Project Source Code

The code for this workshop is available on GitHub: Get The Source Code

Or you can clone the project:

MacBook-Pro:~ user$ git clone https://github.com/tikalk/flutter_workshop.git

Open Workshop Project

  1. Open Android Studio
  2. Close all projects
  3. In the welcome screen select Open an existing Android Studio Project

  1. Select the cloned project directory and click Open

  1. The workshop project should be opened.
  2. Set the project root to be Source Root folder:

Create the Application file

  1. Under the lib folder, find the application.dart file
  2. Open the application file, import the package:flutter/material.dart package
  3. Create MyApp class and extend StatelessWidget
  4. Override the build()method and return a MaterialApp Widget
  5. In the MaterialApp constructor, add title: "Flutter Workshop"
  6. Set the home: argument to Container()
import 'package:flutter/material.dart';

class MyApp extends StatelessWidget{
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     title: "Flutter workshop",
     home: Container(),
   );
 }
}
  1. Under the lib folder, create a main.dart file
  2. Import the material package as in section 2
  3. In the main file, add main() function and call the runApp function with MyApp() as a parameter:
import 'package:flutter/material.dart';
import 'package:flutter_workshop/application.dart';

void main() => runApp(MyApp());
  1. Open Android Studio terminal, run the app by typing:
    flutter run -d all

What you will do here

Create The HomePage

  1. Under the lib folder, create a new pages package
  2. Inside the new pages package, create home_page package
  3. Under the home_page package, create a new home_page.dart file
  4. Open the home_page.dart file, create the HomePage class, extends StatelessWidget, override the build()method and return a Container()
    (Import the material package as necessary)
import 'package:flutter/material.dart';

class HomePage extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   return Container();
 }
}
  1. Go back to the application.dart replace the Container() with the new HomePage()
    Make import as necessary
return MaterialApp(
 title: "Flutter workshop",
 home: HomePage(),
);
  1. Do Hot Reload - In the terminal type r, app still has black screen.

Home Page Body

  1. In the build method of the HomePage class, replace the Container() widget with a Scaffold() widget
    The Scaffold widget represents a Material Design page structure that can have components such as:
  1. Inside the Scaffold constructor, add the appBar: AppBar()
  2. Set the AppBar title. title: Text("Flutter Workshop")
  3. Inside the Scaffold constructor, under the title: argument, add the body: argument and set it to a Center()Widget
  4. Set the Center child: widget to Text("Hello Flutter")
return Scaffold(
   appBar: AppBar(
     title: Text("Flutter workshop"),
   ),
   body: Center(
     child: Text("Hello Flutter"),
   ),
 );
}
  1. Do hot reload, you should now see the homepage now:

What you will do here

  • Add a RaisedButton widget to the homepage
  • Design button color and style
  • Handle button click event
  • Learn about Column and Row widgets
  • Use IDE shortcuts

  1. In the homescreen, click the Text() child widget of the Center widget, press Alt+Enter
  2. Select ‘Wrap with new widget'

  1. Rename the new widget to RaisedButton(),
    In the RaisedButton constructor, set the child: widget to Text("Login")
  2. Add a comma after the button Text(), press Alt+Enter to reformat the code
    Notice the difference with and without the ,
body: Center(
child: RaisedButton(
  child: Text("Login"),
  ),
)
  1. Do hot reload, notice that the button is wrapped and disabled yet, that's because we didn't set an onPressed callback. We will do it in the next following steps.

Add Padding and Enabling

  1. Click on the RaisedButton, wrap it with Column as done above
  1. Do hot reload, the button will jump to the top of the screen, this because of the Column default behavior
  2. In the Column constructor, above the children: <Widget>[] array, add the following attributes:
    mainAxisAlignment: MainAxisAlignment.center,
    mainAxisSize: MainAxisSize.min,
    crossAxisAlignment: CrossAxisAlignment.stretch,
  3. Do hot reload, the button is now centered again and stretched to all screen width
  4. Let's add some padding to the button, Select the RaisedButton press Alt+Enter
  5. Select Add padding, set the padding to padding: const EdgeInsets.all(20.0)
  6. Do hot reload, the button should now have some padding
  7. Let's enable the button by adding onPressed callback
  8. In the button constructor, under the child: Text(), add onPressed: (){},
  9. Add log statement print("Button clicked");
child: RaisedButton(
 child: Text("Login"),
 onPressed: (){
  print("Button Clicked");
 }, 
), //RaisedButton
  1. Do hot reload, the button should be enabled, click the button and watch the log messages in the console.

Styling the button and text

  1. In the RaisedButton constructor add a color: argument - color: Theme.of(context).primaryColor
  2. Set the button text color: textColor: Theme.of(context).primaryTextTheme.button.color
  3. Do hot reload, see the button and text color.
  4. Don't forget commas, press Alt+Cmd+L to reformat the code if required.
Padding(
 padding: const EdgeInsets.all(20.0),
 child: RaisedButton(
   color: Theme.of(context).primaryColor,
   textColor:Theme.of(context).primaryTextTheme.button.color,
   onPressed: (){
     print("Button Pressed");
   }, //Don't forget the commas
   child: Text("Login"), //<- Yes, this comma too :)
 ), //RaisedButton

What you will do here

Add new package and page Dart file

  1. Under the pages package, create new page package - images_page
  2. In the new package create new dart file: images_page.dart
  3. In the new file, create ImagesPage class, extends StatefulWidget
  4. In the new class, override the createState() method
  5. In the ImagesPage, under the ImagesPage class, create another class _ImagesPageState class that extends State<ImagesPage>
  6. In the State class, override the build() method
  1. In the createState() method, return a new ImagesPageState()
class ImagesPage extends StatefulWidget {
 @override
 State<StatefulWidget> createState() =>  _ImagesPagesState();
}

class _ImagesPagesState extends State<ImagesPage>{
 @override
 Widget build(BuildContext context) {
   return null;
 }
}

ImagesPage body

  1. In the State build() method, return Scaffold() widget.
  2. Set an appBar: AppBar() with title: Text("Images Page")
  3. Set the page body: top widget to be Container() with padding of 8.0 points, this widget will wrap the whole page body and allows to add padding, and other decorations such as background color and border.
body: Container(
   padding: const EdgeInsets.all(8.0),
)
  1. Next, set the Container child: widget to Column()
  2. Set the Column attributes to:
    mainAxisAlignment: MainAxisAlignment.center,
    mainAxisSize: MainAxisSize.max,
    crossAxisAlignment: CrossAxisAlignment.center,
  3. Add an children: <Widget>[] array, this array will contain the column child widgets vertically.
  4. The Scaffold() should now looks like this:
Scaffold(
 appBar: AppBar(
   title: Text("Images page"),
 ),
 body: Container(
   padding: const EdgeInsets.all(8.0),
   child: Column(
     mainAxisSize: MainAxisSize.max,
     mainAxisAlignment: MainAxisAlignment.start,
     crossAxisAlignment: CrossAxisAlignment.center,
     children: <Widget>[

     ],
   ),
 ),
);

Open the ImagesPage

  1. In the home_page.dart file, select the Login RaisedButton
  2. In the onPressed: (){} callback, use the Flutter Navigator to open the new ImagesPage():
    Navigator.of(context).push(MaterialRoutePage(builder: (context){ ImagesPage()}));
onPressed: () {
 print("Button Pressed");
 Navigator.of(context).push(
   MaterialPageRoute(
     builder: (context) {
       return ImagesPage();
     },
   ),
 );
}
  1. Import the ImagesPage() class, use Alt+Enter and do import
  2. Do hot reload r or Hot Restart shift+r
  3. Click the login button to open the ImagesPage
  4. You should now see an empty screen:


What you do here

Image Widget

  1. At the top of the _ImagesPageState class, declare a String member variable:

final String _imageUrl = "https://image.tmdb.org/t/p/w500/xvx4Yhf0DVH8G4LzNISpMfFBDy2.jpg";

  1. Under the children widgets array, add the Image.network() widget
  2. Set the URL to _imageUrl variable
  3. Do hot reload or hot restart
  4. Your page should look like this now:
  5. It might show a yellow and black error, this might happen in small screens as the image overlapping the screen size. We will fix it in the next step.

Fix Image Overlap

  1. Select the Container of the Image widget.
  2. Click Alt+Enter
  3. Select Wrap with new widget
  4. Set the name of the widget to Expanded
  5. Set the image fit to fit: BoxFit.fill
Expanded(
 child: Container(
   padding: const EdgeInsets.all(8),
   child: Image.network(_imageUrl, fit: BoxFit.fill,),
 ),
)

Add Two Buttons

  1. Under the Image.network widget add a Row() widget.
  2. Set the Row attributes and add an empty children<Widget>[] array:
Image.network(
 _imageUrl
), //Image.network
Row(
 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
 mainAxisSize: MainAxisSize.max,
 crossAxisAlignment: CrossAxisAlignment.center,
 children: <Widget>[
 ]
), //Row Always add a comma after the last widget
  1. In the empty <Widget>[] array add two RaisedButton widgets
  2. Set the first button child: to Text("Previous")
  3. Set the second button child: to Text("Next")
...
 children: <Widget>[
   RaisedButton(
        child: Text("Previous"),
   ),
   RaisedButton(
        child: Text("Next"),
   ), 
 ]
), //Row Always add a comma after the last widget
  1. Do hot reload, watch the page with an image and two disabled buttons under it.
  2. The buttons are wrapped, let's make them stretched equally
  3. Select the first RaisedButton, press Alt+Enter select, Wrap with widget
  4. Set the widget name to Expanded
  5. Repeat steps 8-9 for the second button
  6. Do hot reload, the buttons should now be stretched horizontal equally

Add Buttons Padding

  1. Select the first RaisedButton, click on the Flutter Outline tab at the right side of the screen:
  2. Press the Add padding button
  3. Set the padding: padding: const EdgeInsets.all(8.0)
  4. Repeat steps 2-3 for Second button
  5. Do hot reload, buttons should now have some padding,

Expanded(
 child: Padding(
   padding: const EdgeInsets.all(8.0),
   child: RaisedButton(
     child: Text("Previous"),
   ),
 ),
)

Enabling the buttons

  1. The buttons still disabled, this because we haven't set an onPressed callback
  2. Inside the first buttons constructor, add onPressed: argument:

onPressed: (){}

  1. Repeat the last step for the second button
  2. Do hot reload, the buttons should be enabled and clickable
  3. Lets style the pbuttons
  4. In the first button constructor, right above the child: argument, set button color:
    color: Theme.of(context).primaryColor
  5. Next, under the color: argument, set the textColor:
    textColor: Theme.of(context).primaryTextTheme.button.color
RaisedButton(
 color: Theme.of(context).primaryColor
 textColor: Theme.of(context).primaryTextTheme.button.color
 child: .... ,
 onPressed: (){}
)

Extract buttons build to a method

  1. Let's create a method to create the buttons
  2. Select the Padding of the first button
  3. Press Command+Alt+M, ExtractMethod dialog open, set the method name to _buildButton
  4. Set the method return type to Widget
  5. Set the method to accept BuildContext, String and Function() callback

Widget _buildButton( {BuildContext context, String title, VoidCallback onPressed})

  1. Replace the RaisedButton parameters inside the method with the method arguments:
Widget _buildButton(
   {BuildContext context, String title, VoidCallback onPressed}) {
 return Padding(
   padding: const EdgeInsets.symmetric(horizontal: 4),
   child: RaisedButton(
     color: Theme.of(context).primaryColor, //Set the context
     textColor: Theme.of(context).primaryTextTheme.button.color,
     child: Text(title) //Set the title,
     onPressed: onPressed, //Set the callback
   ),
 );
  1. Replace the RaisedButton child in the Buttons Row by calling the _buildButton method for each button.
  2. At the end, the buttons Row should look like this:
children: <Widget>[
 Expanded(
   child: _buildButton(
     context: context,
     title: "Previous",
     onPressed: () {},
   ),
 ),
 Expanded(
   child: _buildButton(
     context: context,
     title: "Next",
     onPressed: () {},
   ),
 ),

What you do here

Add Images List

  1. In the State class, add a list of 3 images URLs:
  2. Next, under the images list, add an index variable: int index = 0;
List<String> _images = [
"https://image.tmdb.org/t/p/w500/xvx4Yhf0DVH8G4LzNISpMfFBDy2.jpg",
"https://image.tmdb.org/t/p/w500/svIDTNUoajS8dLEo7EosxvyAsgJ.jpg",
"https://image.tmdb.org/t/p/w500/iiZZdoQBEYBv6id8su7ImL0oCbD.jpg"
];
int _index = 0;
  1. We will use the index to load an image from the list in the Image.network widget
  2. At the bottom of the State class, add two methods for increment and decrement
void _handleNext() {
      setState(() {
      index++;
    });
   }

void _handlePrevious() {
   setState(() {
   index--;
  });
 }
  1. Notice that in these methods we call the setState((){}). This method is Flutter framework that cause our widget to rebuild with the new state.
  2. In the Image.network widget, replace the _imageUrl with _images[_index]
Image.network(
   images[index],
   fit: BoxFit.fill,
  ),

Call Methods from buttons onPressed

  1. We need to prevent the user from click Next when reaching the last image in the list by setting the onPressed callback to null
  2. We should do the same for the Previous button when reaching the first image in the list.
  3. In the ‘Next' RaisedButton, set the onPressed: callback to null as following:
_buildButton(
    context: context,
    title: "Next",
    onPressed: _index < (_images.length - 1) ? () {
    _handleNext();
   } : null,
  )
  1. In the ‘Previous' RaisedButton, set the onPressed: callback to null as following:
_buildButton(
 context: context,
 title: "Previous",
 onPressed: _index > 0 ? () {
   _handlePrevious();
 } : null,
)
  1. You should now be able to navigate between images which are downloaded from the network!

Codelab Summary

Congratulations!! You've reached the end of this codelab !! Cheers!

In this codelab you've learned the basics of Flutter followed with hands on example app.

Flutter has a lot of predefined Widgets and well known recommended architectures and State Management notions such as:

  1. StreamBuilder
  2. Scoped Model
  3. BLoc
  4. Redux

Recommended Flutter tutorials:

You may find great tutorials for these topics in the following playlist

Flutter Codelabs:

You may find more great Flutter codelabs in the official Flutter codelab page!

Dart pad :

An online dart playground: You can use it for coding and learning Dart online.