Pemrograman Mobile
Muhamad Saad Nurul Ishlah, M.Comp.
Dept. Sistem Informasi & Dept. Ilmu Komputer, Universitas Pakuan
GestureDetector(
onTap: () => print("tapped!"),
child: Text("Tap Me"),
);
Child Widget
GestureDetector
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter GestureDetector Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key? key, required this.title}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool _lights = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Container(
alignment: FractionalOffset.center,
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Icon(
Icons.lightbulb_outline,
color: _lights ? Colors.yellow.shade600 : Colors.black,
size: 160,
),
),
GestureDetector(
onTap: () {
setState(() {
_lights = !_lights;
});
},
child: Container(
color: Colors.yellow.shade600,
padding: const EdgeInsets.all(8),
child: const Text('Toggle Light'),
),
),
],
),
),
),
);
}
}
https://www.youtube.com/watch?v=WhVXkCFPmK4
child: ListView.builder(
shrinkWrap: true,
itemCount: allAddedCities.length,
itemBuilder: (BuildContext context, int index) {
final city = allAddedCities[index];
return Dismissible(
onDismissed: (DismissDirection dir)
=> _handleDismiss(dir, city),
background: Container(
child: Icon(Icons.delete_forever),
decoration: BoxDecoration(color: Colors.red[700]),
),
key: ValueKey(city),
child: CheckboxListTile(
value: city.active,
title: Text(city.name),
onChanged: (bool b) => _handleCityActiveChange(b, city),
),
);
}
),
Ada beberapa widget dengan interaksi di dalamnya, dan Anda kemungkinan akan membuatnya sendiri. Mereka semua mengikuti aturan dasar yang sama:
Form(
key: GlobalKey<FormState>,
child: Column(
children: [
TextFormField(),
TextFormField(),
DropdownButtonFormField(),
FormField(child: Checkbox()),
// ...
Button(
child: Text("Submit"),
onPressed: FormState.save()
// ...
/// Flutter code sample for Form
// This example shows a [Form] with one [TextFormField] to enter an email
// address and an [ElevatedButton] to submit the form. A [GlobalKey] is used here
// to identify the [Form] and validate input.
//
// ![](https://flutter.github.io/assets-for-api-docs/assets/widgets/form.png)
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
decoration: const InputDecoration(
hintText: 'Enter your email',
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ElevatedButton(
onPressed: () {
// Validate will return true if the form is valid, or false if
// the form is invalid.
if (_formKey.currentState!.validate()) {
// Process data.
}
},
child: const Text('Submit'),
),
),
],
),
);
}
}
Kombinasi TextField dan FormField
Padding(
padding: const EdgeInsets.symetric(verical: 8.0),
child: TextFormField(
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true,
autoValidate: true,
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
),
3 Properti penting:
Padding(
padding: const EdgeInsets.symetric(verical: 8.0),
child: TextFormField(
onSaved: (String val) {},
...
autoValidate: bool,
validator: (String val) {},
),
),
return FormField(
onSaved: (val) => _newCity.active = _isDefaultFlag,
enabled: _enabled,
builder: (context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Default city?"),
Checkbox(
value: _isDefaultFlag,
onChanged: (val) {
setState(() => _isDefaultFlag = val);
/...
Secara default sudah disediakan oleh Flutter, tidak perlu menambah library tambahan
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
focusNode: focusNode,
onSaved: (String val) => print(val),
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Optional",
labelText: "State or Territory name",
),
validator: (String val) {
if (val.isEmpty) {
return "Field cannot be left blank";
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true,
// ignore: deprecated_member_use
autovalidate: _formChanged,
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
),
class _AddNewCityPageState extends State<AddNewCityPage> {
City _newCity = City.fromUserInput();
bool _formChanged = false;
bool _isDefaultFlag = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
FocusNode focusNode;
@override
void initState() {
super.initState();
focusNode = FocusNode();
}
@override
void dispose() {
// clean up the focus node when this page is destroyed.
focusNode.dispose();
super.dispose();
}
...
//
...
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
focusNode: focusNode,
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true,
// ignore: deprecated_member_use
autovalidate: _formChanged,
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
),
// tombol submit
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
color: Colors.blue[400],
child: Text("Submit"),
onPressed: _formChanged
? () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
_handleAddNewCity();
Navigator.pop(context);
} else {
FocusScope.of(context).requestFocus(focusNode);
}
}
: null,
),
)
Submit
Nama
Submit
Field tidak boleh kosong
Nama
class AddNewCityPage extends StatefulWidget {
final AppSettings settings;
const AddNewCityPage({Key key, this.settings}) : super(key: key);
@override
_AddNewCityPageState createState() => _AddNewCityPageState();
}
class _AddNewCityPageState extends State<AddNewCityPage> {
City _newCity = City.fromUserInput();
bool _formChanged = false;
bool _isDefaultFlag = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
FocusNode focusNode;
@override
void initState() {
super.initState();
focusNode = FocusNode();
}
@override
void dispose() {
// clean up the focus node when this page is destroyed.
focusNode.dispose();
super.dispose();
}
bool validateTextFields() {
return _formKey.currentState.validate();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Add City",
style: TextStyle(color: AppColor.textColorLight),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Form(
key: _formKey,
onChanged: _onFormChange,
onWillPop: _onWillPop,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true,
// ignore: deprecated_member_use
autovalidate: _formChanged,
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
focusNode: focusNode,
onSaved: (String val) => print(val),
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Optional",
labelText: "State or Territory name",
),
validator: (String val) {
if (val.isEmpty) {
return "Field cannot be left blank";
}
return null;
},
),
),
CountryDropdownField(
country: _newCity.country,
onChanged: (newSelection) {
setState(() => _newCity.country = newSelection);
},
),
FormField(
onSaved: (val) => _newCity.active = _isDefaultFlag,
builder: (context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Default city?"),
Checkbox(
value: _isDefaultFlag,
onChanged: (val) {
setState(() => _isDefaultFlag = val);
},
),
],
);
},
),
Divider(
height: 32.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: FlatButton(
textColor: Colors.red[400],
child: Text("Cancel"),
onPressed: () async {
if (await _onWillPop()) {
Navigator.of(context).pop(false);
}
}),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
color: Colors.blue[400],
child: Text("Submit"),
onPressed: _formChanged
? () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
_handleAddNewCity();
Navigator.pop(context);
} else {
FocusScope.of(context).requestFocus(focusNode);
}
}
: null,
),
)
],
),
],
),
),
),
);
}
void _onFormChange() {
if (_formChanged) return;
setState(() {
_formChanged = true;
});
}
void _handleAddNewCity() {
final city = City(
name: _newCity.name,
country: _newCity.country,
active: true,
);
allAddedCities.add(city);
}
Future<bool> _onWillPop() {
if (!_formChanged) return Future<bool>.value(true);
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text("Are you sure you want to abandon the form? Any changes will be lost."),
actions: <Widget>[
FlatButton(
child: Text("Cancel"),
onPressed: () => Navigator.of(context).pop(false),
textColor: Colors.black,
),
FlatButton(
child: Text("Abandon"),
textColor: Colors.red,
onPressed: () => Navigator.pop(context, true),
),
],
) ??
false;
},
);
}
}
class AddNewCityPage extends StatefulWidget {
final AppSettings settings;
const AddNewCityPage({Key key, this.settings}) : super(key: key);
@override
_AddNewCityPageState createState() => _AddNewCityPageState();
}
class _AddNewCityPageState extends State<AddNewCityPage> {
City _newCity = City.fromUserInput();
bool _formChanged = false;
bool _isDefaultFlag = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
FocusNode focusNode;
@override
void initState() {
super.initState();
focusNode = FocusNode();
}
@override
void dispose() {
// clean up the focus node when this page is destroyed.
focusNode.dispose();
super.dispose();
}
bool validateTextFields() {
return _formKey.currentState.validate();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Add City",
style: TextStyle(color: AppColor.textColorLight),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Form(
key: _formKey,
onChanged: _onFormChange,
onWillPop: _onWillPop,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true,
// ignore: deprecated_member_use
autovalidate: _formChanged,
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
focusNode: focusNode,
onSaved: (String val) => print(val),
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Optional",
labelText: "State or Territory name",
),
validator: (String val) {
if (val.isEmpty) {
return "Field cannot be left blank";
}
return null;
},
),
),
CountryDropdownField(
country: _newCity.country,
onChanged: (newSelection) {
setState(() => _newCity.country = newSelection);
},
),
FormField(
onSaved: (val) => _newCity.active = _isDefaultFlag,
builder: (context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Default city?"),
Checkbox(
value: _isDefaultFlag,
onChanged: (val) {
setState(() => _isDefaultFlag = val);
},
),
],
);
},
),
Divider(
height: 32.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: FlatButton(
textColor: Colors.red[400],
child: Text("Cancel"),
onPressed: () async {
if (await _onWillPop()) {
Navigator.of(context).pop(false);
}
}),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
color: Colors.blue[400],
child: Text("Submit"),
onPressed: _formChanged
? () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
_handleAddNewCity();
Navigator.pop(context);
} else {
FocusScope.of(context).requestFocus(focusNode);
}
}
: null,
),
)
],
),
],
),
),
),
);
}
void _onFormChange() {
if (_formChanged) return;
setState(() {
_formChanged = true;
});
}
void _handleAddNewCity() {
final city = City(
name: _newCity.name,
country: _newCity.country,
active: true,
);
allAddedCities.add(city);
}
Future<bool> _onWillPop() {
if (!_formChanged) return Future<bool>.value(true);
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text("Are you sure you want to abandon the form? Any changes will be lost."),
actions: <Widget>[
FlatButton(
child: Text("Cancel"),
onPressed: () => Navigator.of(context).pop(false),
textColor: Colors.black,
),
FlatButton(
child: Text("Abandon"),
textColor: Colors.red,
onPressed: () => Navigator.pop(context, true),
),
],
) ??
false;
},
);
}
}
class AddNewCityPage extends StatefulWidget {
final AppSettings settings;
const AddNewCityPage({Key key, this.settings}) : super(key: key);
@override
_AddNewCityPageState createState() => _AddNewCityPageState();
}
class _AddNewCityPageState extends State<AddNewCityPage> {
City _newCity = City.fromUserInput();
bool _formChanged = false;
bool _isDefaultFlag = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
FocusNode focusNode;
@override
void initState() {
super.initState();
focusNode = FocusNode();
}
@override
void dispose() {
// clean up the focus node when this page is destroyed.
focusNode.dispose();
super.dispose();
}
bool validateTextFields() {
return _formKey.currentState.validate();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Add City",
style: TextStyle(color: AppColor.textColorLight),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Form(
key: _formKey,
onChanged: _onFormChange,
onWillPop: _onWillPop,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true,
// ignore: deprecated_member_use
autovalidate: _formChanged,
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
focusNode: focusNode,
onSaved: (String val) => print(val),
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Optional",
labelText: "State or Territory name",
),
validator: (String val) {
if (val.isEmpty) {
return "Field cannot be left blank";
}
return null;
},
),
),
CountryDropdownField(
country: _newCity.country,
onChanged: (newSelection) {
setState(() => _newCity.country = newSelection);
},
),
FormField(
onSaved: (val) => _newCity.active = _isDefaultFlag,
builder: (context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Default city?"),
Checkbox(
value: _isDefaultFlag,
onChanged: (val) {
setState(() => _isDefaultFlag = val);
},
),
],
);
},
),
Divider(
height: 32.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: FlatButton(
textColor: Colors.red[400],
child: Text("Cancel"),
onPressed: () async {
if (await _onWillPop()) {
Navigator.of(context).pop(false);
}
}),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
color: Colors.blue[400],
child: Text("Submit"),
onPressed: _formChanged
? () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
_handleAddNewCity();
Navigator.pop(context);
} else {
FocusScope.of(context).requestFocus(focusNode);
}
}
: null,
),
)
],
),
],
),
),
),
);
}
void _onFormChange() {
if (_formChanged) return;
setState(() {
_formChanged = true;
});
}
void _handleAddNewCity() {
final city = City(
name: _newCity.name,
country: _newCity.country,
active: true,
);
allAddedCities.add(city);
}
Future<bool> _onWillPop() {
if (!_formChanged) return Future<bool>.value(true);
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text("Are you sure you want to abandon the form? Any changes will be lost."),
actions: <Widget>[
FlatButton(
child: Text("Cancel"),
onPressed: () => Navigator.of(context).pop(false),
textColor: Colors.black,
),
FlatButton(
child: Text("Abandon"),
textColor: Colors.red,
onPressed: () => Navigator.pop(context, true),
),
],
) ??
false;
},
);
}
}
class AddNewCityPage extends StatefulWidget {
final AppSettings settings;
const AddNewCityPage({Key key, this.settings}) : super(key: key);
@override
_AddNewCityPageState createState() => _AddNewCityPageState();
}
class _AddNewCityPageState extends State<AddNewCityPage> {
City _newCity = City.fromUserInput();
bool _formChanged = false;
bool _isDefaultFlag = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
FocusNode focusNode;
@override
void initState() {
super.initState();
focusNode = FocusNode();
}
@override
void dispose() {
// clean up the focus node when this page is destroyed.
focusNode.dispose();
super.dispose();
}
bool validateTextFields() {
return _formKey.currentState.validate();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
"Add City",
style: TextStyle(color: AppColor.textColorLight),
),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Form(
key: _formKey,
onChanged: _onFormChange,
onWillPop: _onWillPop,
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
onSaved: (String val) => _newCity.name = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Required",
labelText: "City name",
),
autofocus: true,
// ignore: deprecated_member_use
autovalidate: _formChanged,
validator: (String val) {
if (val.isEmpty) return "Field cannot be left blank";
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
focusNode: focusNode,
onSaved: (String val) => print(val),
decoration: InputDecoration(
border: OutlineInputBorder(),
helperText: "Optional",
labelText: "State or Territory name",
),
validator: (String val) {
if (val.isEmpty) {
return "Field cannot be left blank";
}
return null;
},
),
),
CountryDropdownField(
country: _newCity.country,
onChanged: (newSelection) {
setState(() => _newCity.country = newSelection);
},
),
FormField(
onSaved: (val) => _newCity.active = _isDefaultFlag,
builder: (context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Default city?"),
Checkbox(
value: _isDefaultFlag,
onChanged: (val) {
setState(() => _isDefaultFlag = val);
},
),
],
);
},
),
Divider(
height: 32.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: FlatButton(
textColor: Colors.red[400],
child: Text("Cancel"),
onPressed: () async {
if (await _onWillPop()) {
Navigator.of(context).pop(false);
}
}),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
color: Colors.blue[400],
child: Text("Submit"),
onPressed: _formChanged
? () {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
_handleAddNewCity();
Navigator.pop(context);
} else {
FocusScope.of(context).requestFocus(focusNode);
}
}
: null,
),
)
],
),
],
),
),
),
);
}
void _onFormChange() {
if (_formChanged) return;
setState(() {
_formChanged = true;
});
}
void _handleAddNewCity() {
final city = City(
name: _newCity.name,
country: _newCity.country,
active: true,
);
allAddedCities.add(city);
}
Future<bool> _onWillPop() {
if (!_formChanged) return Future<bool>.value(true);
return showDialog<bool>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text("Are you sure you want to abandon the form? Any changes will be lost."),
actions: <Widget>[
FlatButton(
child: Text("Cancel"),
onPressed: () => Navigator.of(context).pop(false),
textColor: Colors.black,
),
FlatButton(
child: Text("Abandon"),
textColor: Colors.red,
onPressed: () => Navigator.pop(context, true),
),
],
) ??
false;
},
);
}
}