Script I used to demo. I will clean this and use proper md syntax 1. Make sure its setup macOS Setup Guide: https://flutter.io/setup-macos Windows Setup Guide: https://flutter.io/setup-windows Linux Setup Guide: https://flutter.io/setup-linux Visual Studio Code: https://code.visualstudio.com/ Visual Studio Code Flutter Extension: https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter Android Studio: https://developer.android.com/studio/
Link to my slides https://docs.google.com/presentation/d/1PEAaLvDouDzg-k8SSQl8EfdqwrUVkiwb6GXT3M7wlOg/edit?usp=sharing
flutter --version
open -a Simulator
Starts up the emulator, installs and opens up your Flutter app.
If you don't know the DeviceID
, you can run this to get the list of your available emulators.
Starts the project on either an emulator or device. Depending on the specified DeviceID
.
To run on Android with more that one build flavor, please specify flavor
.
If you don't know the DeviceID
, you can run this to get the list of your available devices that have already been started or connected to the computer.
Flutter is a toolset and a framework at the same time.
Dartpad for learning basic of dart https://dartpad.dartlang.org/
Sample if needed:
class Person {
String name = 'sumith';
int age = 30;
}
double addNumbers(double num1, double num2) { return num1 + num2; }
void main() {
for (int i = 0; i < 5; i++) {
print('hello ${i + 1}');
}
var p1 = Person();
var p2 = Person();
p2.name ='Andre';
print(p1.name);
print(p2.name);
double addnumberResult = addNumbers(2,3);
print(addnumberResult);
}
Make sure its all working
flutter doctor
flutter create todo_app
flutter run
Show Dart devTools
Android Studio or VSCode
Quick update change the click to 2 times.
All good lets work on the Todo app.
For that we need a task_list
create a folder model
To start with lets create a model name it task.dart
class Task {
String name, details;
bool completed = false;
static List<Task> _tasks = [
Task('Choose topic', 'testing desc'),
Task('Drink coffee', null),
Task('Prepare codelab', null),
Task('Update SDK', 'Flutter'),
];
static List<Task> get tasks => _tasks;
Task(this.name, this.details); // Constructor with parameters for creating a Task object
}
Lets delete every thing in main.dart
main.dart
is the starting point
Talk about the main function -> is the entry point
import 'package:flutter/material.dart';
import 'task_list_view.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: TaskListView(),
);
}
}
So the home points to TaskListView
, lets create a task_list_view.dart
Talk about scaffold
import 'package:flutter/material.dart';
import 'model/task.dart';
class TaskListView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Tasks'),
),
body: ListView(
children: <Widget>[
for (final task in Task.tasks)
ListTile(
leading: IconButton(
icon: (task.completed)
? Icon(Icons.check_circle)
: Icon(Icons.radio_button_unchecked),
onPressed: null,
),
title: Text(task.name),
subtitle: (task.details != null) ? Text(task.details) : null,
),
],
),
);
}
}
We are using a for loop so modify pubspec.yaml
sdk: ">=2.2.2 <3.0.0"
Talk about hotreload, and restart
Now you got the checkbox, lets see if we can reflect these toggle in code Talk about stateful vs stateless widgets
In task_list_view.dart
Change onPressed: () => toggleTask(task),
Change the class extend to StatefulWidget
@override
_TaskListViewState createState() => _TaskListViewState();
}
class _TaskListViewState extends State<TaskListView> {
void toggleTask(Task task) {
setState(() {
task.completed = !task.completed;
});
}
...
}
------------------------ thats pretty easy and awesome check if its break time-----------------
This looks like a long list would be nice if we can seperate our list to the ones completed different
We extract the list in its own list tile widget
Widget _listTile(Task task) {
return ListTile(
leading: IconButton(
icon: (task.completed)
? Icon(Icons.check_circle)
: Icon(Icons.radio_button_unchecked),
onPressed: () => toggleTask(task),
),
title: Text(task.name),
subtitle: (task.details != null) ? Text(task.details) : null,
);
}
`for (final task in Task.tasks) _listTile(task),
Lets modify the model "model.dart" and add method to ... Not the best practice, les make it work.
static List<Task> get currentTasks =>
_tasks.where((task) => !task.completed).toList();
static List<Task> get completedTasks =>
_tasks.where((task) => task.completed).toList();
Now we seperate the current task and completed tasks in the list view
for (final task in Task.currentTasks) _listTile(task),
for (final task in Task.currentTasks) _listTile(task),
Divider(),
ExpansionTile(
key: _completedKey,
title: Text('Completed (${Task.completedTasks.length})'),
children: <Widget>[
for (final task in Task.completedTasks) _listTile(task),
],
)
],
under class _TaskListViewState extends State {
final GlobalKey<State> _completedKey = GlobalKey<State>();
---------------- So we would like to add some tasks to the list --------------
Lets create a new dart file add_task_dialog.dart
-> stateful widget, I will copy some code and explain as i go along.....
import 'package:flutter/material.dart';
import 'model.dart';
class AddTaskDialog extends StatefulWidget {
@override
_TaskDialogState createState() => _TaskDialogState();
}
class _TaskDialogState extends State<AddTaskDialog> {
String _taskName = '';
String _taskDetails;
void _save() {
final task = _taskName.isNotEmpty ? Task(_taskName, _taskDetails) : null;
Navigator.of(context).pop(task);
}
@override
Widget build(BuildContext context) {
final ThemeData theme = Theme.of(context);
return Scaffold(
appBar: AppBar(
title: Text('New Task'),
),
body: Form(
child: ListView(
padding: const EdgeInsets.all(16.0),
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(vertical: 8.0),
alignment: Alignment.bottomLeft,
child: TextField(
decoration: const InputDecoration(
labelText: 'Name',
filled: true,
),
style: theme.textTheme.headline,
onChanged: (value) => _taskName = value,
autofocus: true,
),
),
Container(
padding: const EdgeInsets.symmetric(vertical: 8.0),
alignment: Alignment.bottomLeft,
child: TextField(
decoration: const InputDecoration(
labelText: 'Details',
filled: true,
),
onChanged: (value) => _taskDetails = value,
),
),
],
),
),
);
}
}
We modify our task_list
add a floating action button
floatingActionButton: FloatingActionButton(
onPressed: _addTask,
),
void _addTask() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TaskDialog(),
fullscreenDialog: true,
),
);
}
Lets also change toggleTask
to be private for consistency _toggleTask
We can navigate from tasklist to add task dialog, finally lets add a button to save.
actions: <Widget>[
FlatButton(
child: Text(
'SAVE',
style: theme.textTheme.body1.copyWith(color: Colors.white),
),
onPressed: _save,
),
],
),
When save returns we have to wait for the dialog as it returns task. So lets change the _add task to async and save it to the list
void _addTask() async {
final task = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => TaskDialog(),
fullscreenDialog: true,
),
);
if (task != null) {
setState(() {
Task.tasks.add(task);
});
}
}
--------- finally if we have time lets look at theming and delete
In main.dart
We see theme:
, lets add the dark theme
theme: ThemeData.dark(),
Delete task -> we can play with platform specific dialogs here
Lets add an trailing ICON
subtitle: (task.details != null) ? Text(task.details) : null,
trailing: IconButton(
icon: Icon(Icons.delete_forever),
onPressed: () => _deleteTask(task),
),
import 'dart:io';
import 'package:flutter/cupertino.dart';
void _deleteTask(Task task) async {
final confirmed = (Platform.isIOS)
? await showCupertinoDialog<bool>(
context: context,
builder: (context) => CupertinoAlertDialog(
title: const Text('Delete Task?'),
content: const Text('This task will be permanently deleted.'),
actions: <Widget>[
CupertinoDialogAction(
child: const Text('Delete'),
isDestructiveAction: true,
onPressed: () {
Navigator.of(context).pop(true);
},
),
CupertinoDialogAction(
child: const Text('Cancel'),
isDefaultAction: true,
onPressed: () {
Navigator.of(context).pop(false);
},
),
],
),
)
: await showDialog<bool>(
context: context,
builder: (context) => AlertDialog(
title: const Text('Delete Task?'),
content: const Text('This task will be permanently deleted.'),
actions: <Widget>[
FlatButton(
child: const Text("CANCEL"),
onPressed: () {
Navigator.of(context).pop(false);
},
),
FlatButton(
child: const Text("DELETE"),
onPressed: () {
Navigator.of(context).pop(true);
},
),
],
),
);
if (confirmed ?? false) {
setState(() {
Task.tasks.remove(task);
});
}
}
For consistency lets change floating button to:
For consistency lets change floating button to:
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _addTask,
),