The Complete Flutter Development Bootcamp with Dart - notes
Installing Flutter on Mac
sudo softwareupdate --install-rosetta --agree-to-license
<https://storage.googleapis.com/flutter_infra_release/releases/stable/macos/flutter_macos_arm64_3.7.3-stable.zip>
Install Android Studio
after installing go to the plugin and install Flutter
Install Xcode
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -runFirstLaunch
after installation, there will be a new simulator.app run it
Resources
repo - https://github.com/londonappbrewery/Flutter-Course-Resources
https://www.appicon.co/ to generate icons
https://icons8.com/illustrations download images
https://www.vecteezy.com/ images stock
From Scratch using materialApp design
import 'package:flutter/material.dart';
// the main function is the starting point for all our flutter apps
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('I am Rich'),
backgroundColor: Colors.blueGrey[900],
),
),
),
);
}
import 'package:flutter/material.dart';
// the main function is the starting point for all our flutter apps
void main() {
runApp(MaterialApp(
home: Scaffold(
backgroundColor: Colors.blueGrey,
appBar: AppBar(
title: Text('I am Rich'),
backgroundColor: Colors.blueGrey[900],
),
body: Center(
child: Image(
image: NetworkImage(
'<https://www.oberlo.com/media/1603969791-image-1.jpg?fit=max&fm=jpg&w=1824>'),
),
// image: AssetImage('images/diamond.png'),
),
),
));
}
flutter:
uses-material-design: true
assets:
- images/655.jpg
Stateless widget
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.red,
body: Container(),
),
);
}
}
Padding, Margin, EdgeInsets, SafeArea Widget
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.teal,
body: SafeArea(
child: Container(
height: 100,
width: 100,
//margin: EdgeInsets.all(40),
//margin: EdgeInsets.symmetric(horizontal: 50, vertical: 20),
//margin: EdgeInsets.fromLTRB(30, 10, 50, 20),
margin: EdgeInsets.only(left: 30),
padding: EdgeInsets.all(20),
color: Colors.white,
child: Text('Hello'))),
),
);
}
}
Container, size, and color
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.teal,
body: SafeArea(
child: Column(
//mainAxisSize: MainAxisSize.min,
//verticalDirection: VerticalDirection.up,
//mainAxisAlignment: MainAxisAlignment.center,
//mainAxisAlignment: MainAxisAlignment.spaceEvenly,
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
height: 100,
width: 100,
color: Colors.white,
child: Text('Container 1')),
SizedBox(height: 20),
Container(
height: 100,
width: 200,
color: Colors.blue,
child: Text('Container 2')),
Container(
height: 100,
width: double.infinity,
color: Colors.red,
child: Text('Container 3')),
],
)),
),
);
}
}
Portfolio app
Adding image assets with CircleAvatar, with the name below the picture with some styles
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.teal,
body: SafeArea(
child: Column(
children: <Widget>[
CircleAvatar(
radius: 50,
backgroundImage: AssetImage("assets/user.png"),
),
Text('Madindo',
style: TextStyle(
fontSize: 30,
color: Colors.white,
fontWeight: FontWeight.bold))
],
)),
),
);
}
}
Fonts
https://fonts.google.com/ Pick and download, make directory assets/fonts move there
fonts:
- family: Pacifico
fonts:
- asset: assets/fonts/Pacifico-Regular.ttf
Text('Madindo',
style: TextStyle(
...
fontFamily: 'Pacifico'))
Text('FLUTTER DEVELOPER',
style: TextStyle(
...
letterSpacing: 2.5,
color: Colors.teal.shade100))
Making a container
Container(
color: Colors.white,
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 25),
padding: EdgeInsets.all(10),
child: Row(
children: <Widget>[
Icon(Icons.email, color: Colors.teal.shade900),
SizedBox(width: 10),
Text(
'[email protected]',
style: TextStyle(color: Colors.teal.shade900),
)
],
)
)
Card, ListTile
**Card(**
color: Colors.white,
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 25),
child: ListTile(
leading: Icon(Icons.phone, color: Colors.teal.shade900),
title: Text(
'+628561111111',
style: TextStyle(color: Colors.teal.shade900),
))),
**Card(**
color: Colors.white,
margin: EdgeInsets.symmetric(vertical: 10, horizontal: 25),
child: ListTile(
leading: Icon(Icons.email, color: Colors.teal.shade900),
title: Text(
'[email protected]',
style: TextStyle(color: Colors.teal.shade900),
))),
Expanded, Image.asset vs Image(), flex
Row(
children: <Widget>[
Expanded(
flex: 1,
child: Image.asset('images/dice1.png'),
),
Expanded(
flex: 1,
child: Image(
image: AssetImage('images/dice2.png'),
),
)
],
),
Flutter Outline
Highlight the tree that you want to add center then click on the top right
TextButton
TextButton(
//on press
onPressed: () {
print('Left Button got pressed');
},
// to style a button
ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.green)
)
)
Functions
When expected to get void callback
it means it wants an anonymous function
.
Variables
var leftDiceNumber = 5;
Image.asset('images/dice$leftDiceNumber.png');
Stateless widgets are not really meant to change opposites are stateful widgets.
The state basically just refers to the way things are.
setState
var rightDiceNumber = 1;
child: TextButton(
onPressed: () {
setState(() {
rightDiceNumber = Random().nextInt(6) + 1;
});
},
child: Image.asset('images/dice$rightDiceNumber.png')
),
Function
void changeDiceFace() {
setState(() {
leftDiceNumber = Random().nextInt(6) + 1;
rightDiceNumber = Random().nextInt(6) + 1;
});
}
child: TextButton(
onPressed: () {
setState(() {
changeDiceFace();
});
},
child: Image.asset('images/dice$rightDiceNumber.png')
),
Challenge
Make a ball change
import 'package:flutter/material.dart';
import 'dart:math';
void main() => runApp(
MaterialApp(
home: Scaffold(
backgroundColor: Colors.blue,
appBar: AppBar(
centerTitle: false,
leadingWidth: 0,
title: Text('Ask Me Anything'),
backgroundColor: Colors.blue.shade900,
),
body: BallPage(),
),
),
);
class BallPage extends StatefulWidget {
const BallPage({Key? key}) : super(key: key);
@override
State<BallPage> createState() => _BallPageState();
}
class _BallPageState extends State<BallPage> {
void changeBallnumber() {
setState(() {
ballnumber = Random().nextInt(5) + 1;
});
}
var ballnumber = 1;
@override
Widget build(BuildContext context) {
return Center(
child: Row(
children: <Widget>[
Expanded(
flex: 1,
child: TextButton(
onPressed: () {
changeBallnumber();
},
child: Image.asset('images/ball$ballnumber.png'),
),
)
],
),
);
}
}
Quizzler
https://drive.google.com/open?id=19KAIMVvxu7EupAQkGX4SiBM-Xu5AmGhb&usp=drive_fs
To add a new list of arrays
List<Widget> scoreKeeper = [
Icon(Icons.check, color: Colors.green),
Icon(Icons.close, color: Colors.red),
];
For a typed list of arrays
List<String> myString = ['a','b','c'];
Make question class
class Question {
String questionText;
bool questionAnswer;
Question(String q, bool a) {
questionText = q;
questionAnswer = a;
}
}
make quiz brain to store questions and make them private, if you want to get the question then make a class getQuestionText or getQuestionAnswer
import 'question.dart';
class QuizBrain {
// List<String> questions = [
// 'You can lead a cow down stairs but not up stairs.',
// 'Approximately one quarter of human bones are in the feet.',
// 'A slug\\'s blood is green.'
// ];
//
// List<bool> answers = [
// false,
// true,
// true,
// ];
List<Question> _questionBank = [
Question('Some cats are actually allergic to humans', true),
Question('You can lead a cow down stairs but not up stairs.', false),
Question('Approximately one quarter of human bones are in the feet.', true),
Question('A slug\\'s blood is green.', true),
Question('Buzz Aldrin\\'s mother\\'s maiden name was \\"Moon\\".', true),
Question('It is illegal to pee in the Ocean in Portugal.', true),
Question(
'No piece of square dry paper can be folded in half more than 7 times.',
false),
Question(
'In London, UK, if you happen to die in the House of Parliament, you are technically entitled to a state funeral, because the building is considered too sacred a place.',
true),
Question(
'The loudest sound produced by any animal is 188 decibels. That animal is the African Elephant.',
false),
Question(
'The total surface area of two human lungs is approximately 70 square metres.',
true),
Question('Google was originally called \\"Backrub\\".', true),
Question(
'Chocolate affects a dog\\'s heart and nervous system; a few ounces are enough to kill a small dog.',
true),
Question(
'In West Virginia, USA, if you accidentally hit an animal with your car, you are free to take it home to eat.',
true),
];
String getQuestionText(int questionNumber) {
return _questionBank[questionNumber].questionText;
}
bool getQuestionAnswer(int questionNumber) {
return _questionBank[questionNumber].questionAnswer;
}
}
Adding alert
//https://pub.dev/packages/rflutter_alert
import 'package:rflutter_alert/rflutter_alert.dart';
Alert(
context: context,
title: "Finish",
desc: "You are Done",
type: AlertType.info,
buttons: [
DialogButton(
child: Text(
"Ok",
style: TextStyle(color: Colors.white, fontSize: 20),
),
onPressed: () => Navigator.pop(context),
width: 120,
)
],
).show();
BMI Calculator
adding theme
import 'package:flutter/material.dart';
import 'input_page.dart';
void main() => runApp(BMICalculator());
class BMICalculator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
primaryColor: Color(0xFF0aA0E21),
scaffoldBackgroundColor: Color(0xFF0aA0E21)),
home: InputPage(),
);
}
}
inputPage
import 'dart:ffi';
import 'package:bmi_calculator/screens/results_page.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../components/icon_content.dart';
import '../components/reusable_card.dart';
import '../constants.dart';
import '../components/button_bottom.dart';
import '../components/round_botton.dart';
import 'package:bmi_calculator/calculator_brain.dart';
enum Gender {
male,
female,
}
class InputPage extends StatefulWidget {
@override
_InputPageState createState() => _InputPageState();
}
class _InputPageState extends State<InputPage> {
Gender selectedGender;
int height = 180;
int weight = 60;
int age = 18;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BMI CALCULATOR'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Row(
children: [
Expanded(
child: ReusableCard(
onPress: () {
setState(() {
selectedGender = Gender.male;
});
},
colour: selectedGender == Gender.male
? kActiveCardColour
: kInactiveCardColour,
cardChild:
IconContent(icon: FontAwesomeIcons.mars, label: 'Male'),
),
),
Expanded(
child: ReusableCard(
onPress: () {
setState(() {
selectedGender = Gender.female;
});
},
colour: selectedGender == Gender.female
? kActiveCardColour
: kInactiveCardColour,
cardChild: IconContent(
icon: FontAwesomeIcons.venus, label: 'Female'),
),
),
],
),
),
Expanded(
child: ReusableCard(
colour: kActiveCardColour,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"HEIGHT",
style: kLabelStyle,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.baseline,
textBaseline: TextBaseline.alphabetic,
children: [
Text(
height.toString(),
style: kNumberTextStyle,
),
Text("cm", style: kLabelStyle)
],
),
SliderTheme(
data: SliderTheme.of(context).copyWith(
inactiveTrackColor: Color(0xFF8D8E98),
activeTrackColor: Colors.white,
thumbColor: Color(0xFFEB1555),
overlayColor: Color(0x29EB1555),
thumbShape:
RoundSliderThumbShape(enabledThumbRadius: 15.0),
overlayShape:
RoundSliderOverlayShape(overlayRadius: 30.0)),
child: Slider(
value: height.toDouble(),
min: 120.0,
max: 220.0,
activeColor: Color(0xFFEB15555),
inactiveColor: Color(0xFF8D8E98),
onChanged: (double newValue) {
setState(() {
height = newValue.round();
});
},
),
)
],
),
),
),
Expanded(
child: Row(
children: [
Expanded(
child: ReusableCard(
colour: kActiveCardColour,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('WEIGHT', style: kLabelStyle),
Text(
weight.toString(),
style: kNumberTextStyle,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RoundButton(Icons.add, () {
setState(() {
weight++;
});
}),
SizedBox(width: 10.0),
RoundButton(FontAwesomeIcons.minus, () {
setState(() {
weight--;
});
})
],
)
],
),
)),
Expanded(
child: ReusableCard(
colour: kActiveCardColour,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Age', style: kLabelStyle),
Text(
age.toString(),
style: kNumberTextStyle,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RoundButton(Icons.add, () {
setState(() {
age++;
});
}),
SizedBox(width: 10.0),
RoundButton(FontAwesomeIcons.minus, () {
setState(() {
age--;
});
})
],
)
],
),
)),
],
),
),
BottomButtom(
onTap: () {
CalculatorBrain calc =
CalculatorBrain(height: height, weight: weight);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ResultsPage(
bmiResults: calc.calculateBMI(),
resultText: calc.getResults(),
interpretation: calc.getInterpretation(),
)),
);
},
buttonTitle: 'CALCULATOR',
)
],
),
);
}
}
Result Page
import 'package:bmi_calculator/components/button_bottom.dart';
import 'package:bmi_calculator/screens/input_page.dart';
import 'package:flutter/material.dart';
import '../constants.dart';
import '../components/reusable_card.dart';
class ResultsPage extends StatelessWidget {
//const ({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BMI Calculator'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(
'Your result ',
style: kTitleTextStyle,
),
),
),
Expanded(
flex: 5,
child: ReusableCard(
colour: kActiveCardColour,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Normal',
style: kresultTextStyle,
),
Text(
'18.3',
style: kBmiTextStyle,
),
Text(
'Your BMI result is quite low, you should eat more!',
style: kBodyTextStyle,
textAlign: TextAlign.center,
)
],
),
),
),
BottomButtom(
onTap: () {
Navigator.pop(context);
},
buttonTitle: 'RECALCULATE',
)
],
),
);
}
}
Constants
import 'package:flutter/material.dart';
const kBottomContainerHeight = 80.0;
const kActiveCardColour = Color(0xFF1D1E33);
const kInactiveCardColour = Color(0xFF111328);
const kBottomContainerColour = Color(0xFFEB1555);
enum Gender {
male,
female,
}
const kLabelStyle = TextStyle(
fontSize: 18.0,
color: Color(0xFF8D8E98),
);
const kNumberTextStyle = TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.w900,
);
const kLargeButtonStyle = TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.bold,
);
const kTitleTextStyle = TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.bold,
);
const kresultTextStyle = TextStyle(
color: Color(0xFF24D876),
fontSize: 20.0,
fontWeight: FontWeight.bold,
);
const kBmiTextStyle = TextStyle(
fontSize: 100,
fontWeight: FontWeight.bold,
);
const kBodyTextStyle = TextStyle(
fontSize: 22.0,
);
IconContent (separate file)
import 'package:flutter/material.dart';
import '../constants.dart';
class IconContent extends StatelessWidget {
// const IconContent({
// Key key,
// }) : super(key: key);
final IconData icon;
final String label;
IconContent({this.icon, this.label});
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
icon,
size: 80,
),
SizedBox(
height: 15.0,
),
Text(
label,
style: kLabelStyle,
)
],
);
}
}
ReusableCard (separate file)
import 'package:flutter/material.dart';
class ReusableCard extends StatelessWidget {
// const ReusableCard({
// Key key,
// }) : super(key: key);
ReusableCard({@required this.colour, this.cardChild, this.onPress});
final Color colour;
final Widget cardChild;
final Function onPress;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPress,
child: Container(
child: cardChild,
margin: EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: colour,
),
),
);
}
}
Result Page
import 'package:bmi_calculator/components/button_bottom.dart';
import 'package:bmi_calculator/screens/input_page.dart';
import 'package:flutter/material.dart';
import '../constants.dart';
import '../components/reusable_card.dart';
class ResultsPage extends StatelessWidget {
//const ({Key? key}) : super(key: key);
ResultsPage(
{@required this.bmiResults,
@required this.resultText,
@required this.interpretation});
final String bmiResults;
final String resultText;
final String interpretation;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BMI Calculator'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(15.0),
alignment: Alignment.bottomLeft,
child: Text(
'Your result ',
style: kTitleTextStyle,
),
),
),
Expanded(
flex: 5,
child: ReusableCard(
colour: kActiveCardColour,
cardChild: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
resultText.toUpperCase(),
style: kresultTextStyle,
),
Text(
bmiResults,
style: kBmiTextStyle,
),
Text(
interpretation,
style: kBodyTextStyle,
textAlign: TextAlign.center,
)
],
),
),
),
BottomButtom(
onTap: () {
Navigator.pop(context);
},
buttonTitle: 'RECALCULATE',
)
],
),
);
}
}
Calculator's brain
import 'dart:math';
class CalculatorBrain {
CalculatorBrain({this.height, this.weight});
final int height;
final int weight;
double _bmi;
String calculateBMI() {
_bmi = weight / pow(height / 100, 2);
return _bmi.toStringAsFixed(1);
}
String getResults() {
if (_bmi >= 25) {
return 'Overweight';
} else if (_bmi > 18.5) {
return 'Normal';
} else {
return 'Underweight';
}
}
String getInterpretation() {
if (_bmi >= 25) {
return 'You have a higher than normal body weight. Try to exercise';
} else if (_bmi > 18.5) {
return 'You have a normal body weight. Good job!';
} else {
return 'You have a lower than normal weight. You can eat a bit more';
}
}
}
Round Button
import 'package:flutter/material.dart';
class RoundButton extends StatelessWidget {
final IconData icon;
final Function onPress;
RoundButton(@required this.icon, @required this.onPress);
@override
Widget build(BuildContext context) {
return RawMaterialButton(
fillColor: Color(0xFF4C4F5E),
onPressed: onPress,
child: Icon(icon),
shape: CircleBorder(),
constraints: BoxConstraints.tightFor(width: 56.0, height: 56.0),
);
}
}
Adding theme for Slider()
SliderTheme(
data: SliderTheme.of(context).copyWith(
inactiveTrackColor: Color(0xFF8D8E98),
activeTrackColor: Colors.white,
thumbColor: Color(0xFFEB1555),
overlayColor: Color(0x29EB1555),
thumbShape:
RoundSliderThumbShape(enabledThumbRadius: 15.0),
overlayShape:
RoundSliderOverlayShape(overlayRadius: 30.0)),
child: Slider(
value: height.toDouble(),
min: 120.0,
max: 220.0,
activeColor: Color(0xFFEB15555),
inactiveColor: Color(0xFF8D8E98),
onChanged: (double newValue) {
setState(() {
height = newValue.round();
});
},
),
)
Routes and Navigation
Pretty straightforward about routes, this is probably good in a route file
import 'package:flutter/material.dart';
import 'package:navigation_demo_starter/screen1.dart';
import 'screen0.dart';
import 'screen2.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => Screen0(),
'/first': (context) => Screen1(),
'/second': (context) => Screen2(),
},
);
}
}
import 'package:flutter/material.dart';
class Screen0 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.purple,
title: Text('Screen 0'),
),
body: Center(
child: Column(
children: <Widget>[
ElevatedButton(
child: Text('Go To Screen 1'),
onPressed: () {
//Navigate to Screen 1
Navigator.pushNamed(context, '/first');
},
),
ElevatedButton(
child: Text('Go To Screen 2'),
onPressed: () {
//Navigate to Screen 2
Navigator.pushNamed(context, '/second');
},
),
],
),
),
);
}
}
import 'package:flutter/material.dart';
import 'screen2.dart';
class Screen1 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
title: Text('Screen 1'),
),
body: Center(
child: ElevatedButton(
child: Text('Go Forwards To Screen 2'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Screen2()),
);
},
),
),
);
}
}
import 'package:flutter/material.dart';
import 'screen0.dart';
class Screen2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.blue,
title: Text('Screen 2'),
),
body: Center(
child: ElevatedButton(
child: Text('Go Back To Screen 1'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
}
Map
Clima Flutter App
Get geolocator package
// pubspec.yaml
geolocator: ^10.0.0
void getLocation() **async** {
Position position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.low);
print(position);
}
ElevatedButton(
onPressed: () {
//Get the current location
getLocation();
},
),
Asking permission
android/app/src/main/manifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
ios/Runner/Info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs access to location when open.</string>
double latitude;
double longitude;
void getLocation() async {
LocationPermission permission;
permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.deniedForever) {
return Future.error('Location Not Available');
}
}
Location location = Location();
await location.getCurrentLocation();
latitude = location.latitude;
longitude = location.longitude;
}
Get Http Package
dependencies:
http: ^1.1.0
import 'package:http/http.dart' as http;
const apiKey = "123";
void getData() async {
http.Response response = await http.get(Uri.parse(
'<https://api.openweathermap.org/data/2.5/weather?lat=$latitude&lon=$longitude&appid=$apiKey>'));
if (response.statusCode == 200) {
String data = response.body;
print(data);
var decodedData = jsonDecode(data);
double temperature = decodedData['main']['temp'];
int condition = decodedData['weather'][0]['id'];
String cityName = decodedData['name'];
} else {
print(response.statusCode);
}
}
Adding spinner
Navigator.push(context, MaterialPageRoute(builder: (context) {
return LocationScreen();
});
flutter_spinkit | Flutter Package
Scaffold(
body:Center(
child: SpinKitDoubleBounce(
color: Colors.white,
size: 100.0,
)
)
)
Passing an object
var weatherData = await networkHelper.getData();
Navigator.push(context, MaterialPageRoute(builder: (context) {
return LocationScreen(locationWeather: weatherData);
}));
push the variable to LocationScreen then accept variable
class LocationScreen extends StatefulWidget {
LocationScreen({this.locationWeather});
final locationWeather;
@override
_LocationScreenState createState() => _LocationScreenState();
}
then to get locationWeather from the widget from state
WeatherModel weatherModel = WeatherModel();
int temp = 0;
String weatherIcon = '';
String city = '';
@override
void initState() {
super.initState();
updateUI(widget.locationWeather);
}
void updateUI(dynamic weatherData) {
double temp_1 = weatherData['main']['temp'];
temp = temp_1.toInt();
var cond = weatherData['weather'][0]['id'];
weatherIcon = weatherModel.getWeatherIcon(cond);
city = weatherData['name'];
}
how to pass back navigation pop with variable
Navigator.pop(context, cityName);
// retrieve
onPressed() async {
var typedName = await Navigator.push(context,
MaterialPageRoute(builder: (context) {
return CityScreen();
}));
}
Bitcoin Ticker
const List<String> currenciesList = [
'AUD',
'ZAR'
];
const List<String> cryptoList = [
'BTC',
'ETH',
'LTC',
];
class CoinData {}
Dropdown using DropdownButton (android)
import 'package:bitcoin_ticker/coin_data.dart';
String dropdownValue = currenciesList.first;
Container(
child: DropdownButton<String>(
value: dropdownValue,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.white),
underline: Container(
height: 2,
color: Colors.white,
),
onChanged: (String? value) {
// This is called when the user selects an item.
setState(() {
dropdownValue = value!;
});
},
items:
currenciesList.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
Dropdown using Cupertino (ios)
List<Text> getListItems() {
List<Text> pickerItem = [];
for (String currency in currenciesList) {
pickerItem.add(Text(currency));
}
return pickerItem;
}
Container(
height: 150.0,
alignment: Alignment.center,
padding: EdgeInsets.only(bottom: 30.0),
color: Colors.lightBlue,
child: CupertinoPicker(
backgroundColor: Colors.lightBlue,
itemExtent: 32,
onSelectedItemChanged: (selectedIndex) {
print(selectedIndex);
},
children: getListItems(),
),
),
Change between IOS and Android
import 'dart:io' show Platform;
Platform.isIOS ? iOSPicker() : androidDropdown()
Named routes more efficient
MaterialApp(
...
initialRoute: WelcomeScreen.id,
routes: {
WelcomeScreen.id: (context) => WelcomeScreen(),
LoginScreen.id: (context) => LoginScreen(),
RegistrationScreen.id: (context) => RegistrationScreen(),
ChatScreen.id: (context) => ChatScreen(),
});
class WelcomeScreen extends StatefulWidget {
static String id = 'welcome_screen';
@override
_WelcomeScreenState createState() => _WelcomeScreenState();
}
Hero Animation
// first screen
Hero(
tag: 'logo',
child: Container(
child: Image.asset('images/logo.png'),
height: 60.0,
)),
// second screen
Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
)),
Animating something
AnimationController? controller;
@override
void initState() {
// TODO: implement initState
super.initState();
controller = AnimationController(
duration: Duration(seconds: 1), vsync: this, upperBound: 100.0);
controller?.forward();
controller?.addListener(() {
setState(() {
print(controller?.value);
});
});
}
// animate the logo
Hero(
tag: 'logo',
child: Container(
child: Image.asset('images/logo.png'),
height: controller?.value,
)),
to make it loop
@override
void initState() {
// TODO: implement initState
super.initState();
controller =
AnimationController(duration: Duration(seconds: 1), vsync: this);
animation = CurvedAnimation(parent: controller, curve: Curves.decelerate);
controller.forward();
animation?.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse(from: 1.0);
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
//controller.reverse(from: 1.0);
controller.addListener(() {
setState(() {
print(animation?.value);
});
});
}
@override
void dispose() {
// TODO: implement dispose
controller.dispose();
super.dispose();
}
Change Color
@override
void initState() {
// TODO: implement initState
super.initState();
controller =
AnimationController(duration: Duration(seconds: 3), vsync: this);
animation =
ColorTween(begin: Colors.red, end: Colors.blue).animate(controller);
controller.forward();
controller.addListener(() {
setState(() {
print(animation?.value);
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: animation?.value,
...)
}
Animated text package
animated_text_kit | Flutter Package
animated_text_kit: ^4.2.2 // to pubspec.yaml
import 'package:animated_text_kit/animated_text_kit.dart'; // top
TypewriterAnimatedTextKit(
text: ['Flash Chat'],
textStyle: TextStyle(
fontSize: 45.0,
fontWeight: FontWeight.w900,
),
),
Refactor the code
highlight the function then right-click refactor then choose the extract flutter method
import "package:flutter/material.dart";
class RoundedButton extends StatelessWidget {
RoundedButton({this.title, this.colour, required this.onPressed});
final Color? colour;
final String? title;
final VoidCallback onPressed; // not function
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Material(
elevation: 5.0,
color: colour,
borderRadius: BorderRadius.circular(30.0),
child: MaterialButton(
onPressed: onPressed,
minWidth: 200.0,
height: 42.0,
child: Text(
title.toString(),
),
),
),
);
}
}
use copyWith when want to change the value of the const
const kTextFieldDecoration = InputDecoration(
hintText: 'Enter a value',
contentPadding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.lightBlueAccent, width: 1.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.lightBlueAccent, width: 2.0),
borderRadius: BorderRadius.all(Radius.circular(32.0)),
),
);
kTextFieldDecoration.copyWith(
hintText: "Enter your password"),
)
Firebase (not finished)
import 'package:firebase_auth/firebase_auth.dart';
class _RegistrationScreenState extends State<RegistrationScreen> {
...
final _auth = FirebaseAuth.instance;
...
RoundedButton(
title: "Register",
colour: Colors.blueAccent,
onPressed: () async {
//Go to login screen.
try {
final newUser = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
if (newUser != null) {
Navigator.pushNamed(context, ChatScreen.id);
}
} catch (e) {
print(e);
}
}),
}
import 'package:firebase_auth/firebase_auth.dart';
class _ChatScreenState extends State<ChatScreen> {
final _auth = FirebaseAuth.instance;
User? loggedInUser;
@override
void initState() {
// TODO: implement initState
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = await _auth.currentUser;
if (user != null) {
loggedInUser = user;
print(loggedInUser?.email);
}
} catch (e) {
print(e);
}
}
}
State Management
Checkbox state
class TaskTile extends StatefulWidget {
@override
State<TaskTile> createState() => _TaskTileState();
}
class _TaskTileState extends State<TaskTile> {
bool isChecked = false;
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(
'This is a tasks',
style: TextStyle(
decoration: isChecked ? TextDecoration.lineThrough : null),
),
trailing: TaskCheckbox(isChecked, (bool? checkboxState) {
setState(() {
isChecked = checkboxState ?? true;
});
}),
);
}
}
class TaskCheckbox extends StatelessWidget {
final bool checkboxState;
final Function(bool?) toggleCheckboxState;
TaskCheckbox(this.checkboxState, this.toggleCheckboxState);
@override
Widget build(BuildContext context) {
return Checkbox(
value: checkboxState,
activeColor: Colors.lightBlueAccent,
onChanged: toggleCheckboxState,
);
}
}
Provider
https://pub.dev/packages/provider
dependencies:
provider: ^6.0.5
import 'package:provider/provider.dart';
//wrap the app to provider
// 1st example
FinalString data = "Top secret data";
Widget build(BuildContext context) {
return Provider<String>(
builder: (context) => Data();
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(data)
),
),
),
);
}
// 2nd example front Data()
Widget build(BuildContext context) {
return ChangeNotifierProvider<Data>(
builder: (context) => Data();
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(data)
),
),
),
);
}
class MyTextField extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TextField(
onChanged: (newText) {
Provider.of<Data>(context).changeString(newText);
}
);
}
}
class Level3 extends StatelessWidget {
// 1st example
@override
Widget build(BuildContext context) {
return Text(Provider.of<String>(context));
}
// 2nd example
@override
Widget build(BuildContext context) {
return Text(Provider.of<Data>(context).data);
}
}
class Data extends ChangeNotifier {
String data = "Top secret data";
void changeString(String newString) {
data = newString;
notifyListeners();
}
}
the use of consumer for provider
return ChangeNotifierProvider(
create: (context) => StudentData(),
child: MaterialApp()
);
Consumer<TaskData>(
builder: (context, taskData, child) {
return ListView.builder(
itemBuilder: (context, index) {
return TaskTile(
taskTitle: taskData.tasks[index].name,
isChecked: taskData.tasks[index].isDone,
checkboxCallback: (checkboxState) {
// setState(() {
// Provider.of<TaskData>(context).tasks[index].toggleDone();
// });
});
},
itemCount: taskData.taskCount,
);
},
);
To add updates, delete data from the provider
needs to be in a method because needs to call notifyListeners();
// task_data
// the wrong way
Provider.of<TaskData>(context, listen: false).tasks.add(value);
// the right way
Provider.of<TaskData>(context, listen: false).addTask(newTaskTitle);
void addTask(String newTaskTitle) {
final task = Task(name: newTaskTitle);
tasks.add(task);
notifyListeners();
}
void updateTask(Task task) {
task.toggleDone();
notifyListeners();
}