最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 使用 Flutter 构建一个名言名句应用程序

    正文概述 掘金(Hoarfroster)   2021-04-28   437

    引言

    在过去的 8 个月里,我一直在探索 Flutter。今天我将带着大家开始一段旅程,制作一个属于自己的简单而又漂亮的应用,并同时学会进行 API 请求。

    目录 TOC

    让我们开始……

    初步设置

    在我们深入研究之前,不要忘记将这些包添加到你的项目中:

    animated_text_kit: ^3.1.0
    google_fonts: ^1.1.1
    http: ^0.12.2
    

    创建一个新的 Flutter 项目

    打开你最喜欢的 IDE(VScode 或 Android Studio),创建一个新的 Flutter 应用,并给它取一个你喜欢的名字,保存在本地磁盘的某个地方。

    首先我们删除掉默认生成的计数器应用代码,并创建一个主函数来运行我们的 Material 应用程序。

    // main.dart
    
    import 'package:flutter/material.dart';
    import 'package:flutter_quote_app/screens/home.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      // 你的应用程序 Widget 树的根 Widget
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
            title: '名言名句应用程序',
            theme: ThemeData(
              primarySwatch: Colors.amber,
              visualDensity: VisualDensity.adaptivePlatformDensity,
            ),
            debugShowCheckedModeBanner: false,
            home: HomeScreen());
      }
    }
    

    通过 API 获取数据

    为了获得数据,我们需要一个 API 以获得名言名句的 JSON 格式的原始数据。

    获取数据的 API 是 https://zenquotes.io/api/quotes。当你打开这个链接时,它会向你显示名言名句的原始数据。选择所有的文本复制一下,然后在另一个标签页中打开 quicktype 并粘贴进去,我们就可以立即生成我们的Dart Model

    使用 Flutter 构建一个名言名句应用程序

    将原始数据复制到左侧栏中,并给模型类命名:

    // 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,
        };
    }
    

    以上是生成的 Dart 模型类。

    让我们创建一个函数来请求 API 获取数据

    // 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('失败加载名言名句');
      }
    }
    

    在上面的代码中,我们有一个异步方法,作出一个 GET 请求。如果响应的状态码是 200,它就会返回名言名句的列表,否则就抛出一个异常。

    设计 UI

    现在我们已经准备好了所有的东西,让我们做一个漂亮的 UI 来显示我们的名言名句。

    所以,首先要创建一个有状态的 Widget 作为 HomeScreen 界面。它将有一个 Widget 构建方法(返回一个 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> {
      // 调用 API 并获取数据
      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('获取名言名句失败');
        }
      }
    
      @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,
                ),
              ),
            ],
          ),
        );
      }
    }
    

    在这里,我们使用 Column 作为 Scaffoldbody 属性值,包含两个 Expanded 子 Widget,一个有 flex: 8,另一个没有 flex 不过有一个 Container 作为子 Widget。

    另外,如果你看到上面的代码,我们有一个 void 返回值的 initState。它将在我们导航到 HomeScreen 时运行。它有我们的 fetchQuotes 方法,在 Widget 被插入到树中之前被调用。

    由于我们的 UI 会在应用运行后立即构建,但我们却无法立刻获取到来自 API 的响应,因此如果你的 UI 依赖于 API 响应值,那么它将会抛出很多的 null 错误。

    让我们投奔 Future

    FutureBuilder 也是一个 Widget,因此我们可以直接在我们的 Scaffold 上使用它,或者也可以把它作为一个子 Widget 连接到任何你喜欢的 Widget 上。在这里我将使用 Expanded Widget 作为 FutureBuilder 的父 Widget。

    // 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> {
      // 调用 API 并获取数据
      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('失败加载名言名句');
        }
      }
    
      @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,
                ),
              ),
            ],
          ),
        );
      }
    

    FutureBuilder 有两个主要属性:futurebuilder。这里我们将 future 赋值为 fetchQuotes() 方法以运行获取数据的函数并将结果返回给 buildersnapshot。现在只要用给出的结果创建任何你喜欢的 Widget 即可。

    现在我想要的是这样的行为:当我在等待结果的时候,我想向用户显示一个 CircularProgressIndicator。而一旦有了返回的数据就立即显示名言名句页面。

    FutureBuilder 能帮助我们轻松实现:

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

    在这里,我们已经创建了我的 PageView Widget 构造器 buildPageView(),并将其传递给子 Widget。

    样式化文本

    // 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(
                    '名言名句应用程序',
                    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),
                    ),
                  ),
                ],
              ),
            );
          },
        );
      }
    }
    

    在 PageView 构造器中,我们使用了 TyperAnimatedTextKit,而你也需要导入这个包。在 TyperAnimatedTextKit 里面有一个函数,可以帮助我们在屏幕上输入完整的字符串时跳到下一页。另外,我们使用了 Google Fonts,你也需要导入同样的包。

    // 等待上传 // miro.medium.com/max/1200/1*…

    瞧! 您已经创建了第一个名言名句应用程序。

    本文代码:flutter-devs/flutter_quote_app



    起源地下载网 » 使用 Flutter 构建一个名言名句应用程序

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元