• Quotes App
  • Anubhav Gupta
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…

The introduction

For the past 8 months, I have been exploring Flutter. Today I’m going to take you on a journey to make your own simple and beautiful application and learn how to make API requests at the same time.

Directory TOC

Get data from the Internet. Design the user interface of your application. Style the text.

Let’s get started…

Initial setup

Before we delve further, don’t forget to add these packages to your project:

animated_text_kit: ^ 3.1.0
google_fonts: ^ 1.1.1
http: ^ 0.12.2
Copy the code

Create a new Flutter project

Open your favorite IDE (VScode or Android Studio), create a new Flutter application, give it a name you like, and save it somewhere on your local disk.

First we remove the default generated counter application code and create a main function to run our Material application.

// main.dart

import 'package:flutter/material.dart';
import 'package:flutter_quote_app/screens/home.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // The root Widget of your app's Widget tree
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Famous Quotes App',
        theme: ThemeData(
          primarySwatch: Colors.amber,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        debugShowCheckedModeBanner: false, home: HomeScreen()); }}Copy the code

Get the data through the API

To get the data, we need an API to get the raw data for the quote in JSON format.

To get the data API is https://zenquotes.io/api/quotes. When you open this link, it will show you the raw data for the quote. Select all the text and copy it, then open QuickType in another TAB and paste it in, and we can immediately generate our Dart Model.

Copy the original data into the left column and name the model class:

// quotesmodel.dart

import 'dart:convert';

List<Quotes> quotesFromJson(String str) =>
  List<Quotes>.from(json.decode(str).map((x) => Quotes.fromJson(x)));

String quotesToJson(List<Quotes> data) =>
  json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class Quotes {
  Quotes({
    this.q,
    this.a,
    this.h,
  });

  String q;
  String a;
  String h;

  factory Quotes.fromJson(Map<String.dynamic> json) => Quotes(
      q: json["q"],
      a: json["a"],
      h: json["h"]);Map<String.dynamic> toJson() => {
      "q": q,
      "a": a,
      "h": h,
    };
}
Copy the code

These are the generated Dart model classes.

Let’s create a function that asks the API to get the data

// home.dart

static Future<List<Quotes>> fetchQuotes() async {
  final response = await http.get('https://zenquotes.io/api/quotes');
  if (response.statusCode == 200) {
    print(quotesFromJson(response.body).length);
    return quotesFromJson(response.body);
  } else {
    throw Exception('Failed to load quotes'); }}Copy the code

In the above code, we have an asynchronous method that makes a GET request. If the status code of the response is 200, it returns a list of quotes, otherwise it throws an exception.

Design the UI

Now that we have everything ready, let’s make a nice UI to display our famous quotes.

So, the first step is to create a stateful Widget as the HomeScreen interface. It will have a Widget build method that returns a Scaffold.

// home.dart

import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quote_app/models/qoutemodel.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

PageController pageController = PageController(keepPage: true);

class _HomeScreenState extends State<HomeScreen> {
  // Call API and get data
  static Future<List<Quotes>> fetchQuotes() async {
    final response = await http.get('https://zenquotes.io/api/quotes');
    if (response.statusCode == 200) {
      print(quotesFromJson(response.body).length);
      return quotesFromJson(response.body);
    } else {
      throw Exception('Failed to get quotes'); }}@override
  void initState() {
    super.initState();
    fetchQuotes();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            flex: 8,
            child: /* TODO */), Expanded( child: Container( alignment: Alignment.center, ), ), ], ), ); }}Copy the code

In this case, we use Column as the body property value of the Scaffold and contain two Expanded child widgets, one with Flex: 8 and the other with no Flex but a Container as the child Widget.

Also, if you see the code above, we have an initState for the return value of void. It will run when we navigate to HomeScreen. It has our fetchQuotes method, which is called just before the Widget is inserted into the tree.

Since we are using an API, we will use FutureBuilder.

Why use this thing?

Since our UI is built immediately after the application runs, we don’t get the response from the API right away, so if your UI depends on the API response value, it will throw a lot of NULL errors.

Let’s go to Future

FutureBuilder is also a Widget, so we can use it directly on our Scaffold or connect it as a child Widget to any Widget you like. Here I will use Expanded Widget as the parent Widget of FutureBuilder.

// home.dart

import 'package:animated_text_kit/animated_text_kit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quote_app/models/qoutemodel.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:http/http.dart' as http;

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

PageController pageController = PageController(keepPage: true);

class _HomeScreenState extends State<HomeScreen> {
  // Call API and get data
  static Future<List<Quotes>> fetchQuotes() async {
    final response = await http.get('https://zenquotes.io/api/quotes');
    if (response.statusCode == 200) {
      print(quotesFromJson(response.body).length);
      return quotesFromJson(response.body);
    } else {
      throw Exception('Failed to load quotes'); }}@override
  void initState() {
    super.initState();
    fetchQuotes();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          Expanded(
            flex: 8,
            child: FutureBuilder<List<Quotes>>(
              future: fetchQuotes(),
              builder:
                  (BuildContext context, AsyncSnapshot<List<Quotes>> snapshot) {
                if (snapshot.connectionState == ConnectionState.done &&
                    snapshot.hasData) {
                  return buildPageView(snapshot);
                } else {
                  return Center(child: CircularProgressIndicator());
                }
              },
            ),
          ),
          Expanded(
            child: Container(
              alignment: Alignment.center,
            ),
          ),
        ],
      ),
    );
  }
Copy the code

FutureBuilder has two main properties: Future and Builder. Here we assign the Future to the fetchQuotes() method to run the function that gets the data and returns the result to the Snapshot of the Builder. Now just create any Widget you like with the results.

Now what I want is this: when I am waiting for the result, I think a CircularProgressIndicator displayed to the user. The quote page is displayed as soon as the returned data is available.

FutureBuilder can help us easily achieve:

future: fetchQuotes(),          
builder: (BuildContext context, AsyncSnapshot<List<Quotes>> snapshot) {
    if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
       return buildPageView(snapshot);
    } else {
       returnCenter(child: CircularProgressIndicator()); }}Copy the code

Here, we’ve created my PageView Widget constructor buildPageView() and passed it to the child widgets.

Styled text

// home.dart

PageView buildPageView(AsyncSnapshot<List<Quotes>> snapshot) {
    return PageView.builder(
      controller: pageController,
      itemCount: snapshot.data.length,
      scrollDirection: Axis.vertical,
      itemBuilder: (BuildContext context, int index) {
        return Container(
          // height: mediaquery.of (context).size. Height * 0.87,
          width: double.infinity,
          decoration: BoxDecoration(
            color: Colors.amberAccent[700],
            borderRadius: BorderRadius.only(
              topLeft: Radius.circular(20),
              bottomLeft: Radius.circular(60),
            ),
          ),
          padding: EdgeInsets.symmetric(horizontal: 20, vertical: 30),
          margin: EdgeInsets.only(bottom: 10),

          child: Stack(
            children: [
              Text(
                'Famous Quotes App',
                style: GoogleFonts.lobster(fontSize: 45, color: Colors.white),
              ),
              Align(
                alignment: Alignment.center,
                child: TyperAnimatedTextKit(
                  isRepeatingAnimation: false,
                  repeatForever: false,
                  displayFullTextOnTap: true,
                  speed: const Duration(milliseconds: 150),
                  onFinished: () {
                    pageController.nextPage(
                      duration: Duration(seconds: 1),
                      curve: Curves.easeInOutCirc,
                    );
                  },
                  text: ['"' + snapshot.data[index].q + '"'],
                  textStyle: GoogleFonts.montserratAlternates(
                      fontSize: 30.0, color: Colors.white),
                ),
              ),
              Align(
                alignment: Alignment.bottomRight,
                child: Text(
                  snapshot.data[index].a,
                  style: GoogleFonts.lora(fontSize: 14(() [() [() [() [() }); }}Copy the code

In the PageView constructor, we use TyperAnimatedTextKit, and you need to import this package as well. There is a function in the TyperAnimatedTextKit that will help us skip to the next page when we type a complete string on the screen. In addition, we use Google Fonts, you need to import the same package.

/ / wait for the upload / / miro.medium.com/max/1200/1 *…

Look! You have created your first quote application.

This article code: flutter-devs/flutter_quote_app

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.