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'),
            ),
          )
        ],
      ),
    );
  }
}

Dart packages

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

http | Dart 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 {}
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(),
    ),
  ),
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();
}

Subscribe to You Live What You Learn

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
[email protected]
Subscribe