携手创造,共同生长!这是我参与「日新计划 8 月更文应战」的第17天,点击查看活动详情

新建项目,参加图片字体,编写欢迎界面

新建项目

flutter create jimmy_flutter_demo

参加图片字体

在根目录上新建一个 assets 文件夹

assets
  fonts // 存放字体
  images // 存放图片

pubspec.yaml 文件设定 images 的途径内容:

assets:
  - assets/images/

pubspec.yaml 文件设定 fonts 的途径内容:

fonts:
  - family: Avenir
    fonts:
      - asset: assets/fonts/Avenir-Book.ttf
        weight: 400
  - family: Montserrat
    fonts:
      - asset: assets/fonts/Montserrat-SemiBold.ttf
        weight: 600

编写欢迎页面

增加屏幕适配的包。

  # 屏幕适配
  flutter_screenutil: ^1.0.2

拉取新包:flutter pub get 获取直接装置 flutter pub add flutter_screenutil

设定屏幕见 lib/common/utils/screen.dart

设定这个 app 的一些色彩,见 lib/common/values/colors.dart

增加欢迎页面 lib/pages/welcome/welcomePage.dart

更改入口文件 lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:jimmy_flutter_demo/pages/welcome/welcomePage.dart';
void main() => runApp(MyApp());
// 查看 https://github.com/OpenFlutter/flutter_screenutil/blob/master/README_CN.md
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    //填入设计稿中设备的屏幕尺寸,单位dp
    return ScreenUtilInit(
      designSize: const Size(360, 690),
      minTextAdapt: true,
      splitScreenMode: true,
      builder: (context, child) {
        return MaterialApp(
          debugShowCheckedModeBanner: false,
          title: 'First Method',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            textTheme: Typography.englishLike2018.apply(fontSizeFactor: 1.sp),
          ),
          home: child,
        );
      },
      child: const WelcomePage(),
    );
  }
}

这里需要对 flutter_screenutil 做全局的引进。

然后对欢迎页面进行增加,内容如下:

import 'package:flutter/material.dart';
import 'package:jimmy_flutter_demo/common/utils/utils.dart';
import 'package:jimmy_flutter_demo/common/values/values.dart';
class WelcomePage extends StatelessWidget {
  const WelcomePage({Key? key}) : super(key: key);
  // 页头标题
  Widget _buildPageHeaderTitle() {
    return Container(
      margin: EdgeInsets.only(top: duSetHeight(65)),
      child: Text(
        "Features",
        textAlign: TextAlign.center,
        style: TextStyle(
          color: AppColors.primaryText,
          fontFamily: "Montserrat",
          fontWeight: FontWeight.w600,
          fontSize: duSetFontSize(24),
        ),
      ),
    );
  }
  // 页头阐明
  Widget _buildPageHeaderDetail() {
    return Container(
      width: duSetWidth(242),
      height: duSetHeight(70),
      margin: EdgeInsets.only(top: duSetHeight(14)),
      child: Text(
        "The best of news channels all in one place. Trusted sources and personalized news for you.",
        textAlign: TextAlign.center,
        style: TextStyle(
          color: AppColors.primaryText,
          fontFamily: "Avenir",
          fontWeight: FontWeight.normal,
          fontSize: duSetFontSize(16),
          height: 1.3,
        ),
      ),
    );
  }
  // 特性阐明
  // 宽度 80 + 20 + 195 = 295
  Widget _buildFeatureItem(String imageName, String intro, double marginTop) {
    return Container(
      width: duSetWidth(295),
      height: duSetHeight(80),
      margin: EdgeInsets.only(top: duSetHeight(marginTop)),
      child: Row(
        children: [
          Container(
            width: duSetWidth(80),
            height: duSetHeight(80),
            child: Image.asset(
              "assets/images/$imageName.png",
              fit: BoxFit.none,
            ),
          ),
          const Spacer(),
          Container(
            width: duSetWidth(195),
            child: Text(
              intro,
              textAlign: TextAlign.center,
              style: TextStyle(
                color: AppColors.primaryText,
                fontFamily: "Avenge",
                fontWeight: FontWeight.normal,
                fontSize: duSetFontSize(16),
              ),
            ),
          ),
        ],
      ),
    );
  }
  // 开始按钮
  Widget _buildStartButton() {
    return Container(
      width: duSetWidth(295),
      height: duSetHeight(44),
      margin: EdgeInsets.only(bottom: duSetHeight(20)),
      child: TextButton(
        onPressed: () => {},
        style: ButtonStyle(
          backgroundColor: MaterialStateProperty.all(AppColors.primaryElement),
          textStyle: MaterialStateProperty.all(const TextStyle(
            color: AppColors.primaryElementText,
          )),
        ),
        child: const Text("Get started"),
      ),
    );
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: <Widget>[
            _buildPageHeaderTitle(),
            _buildPageHeaderDetail(),
            _buildFeatureItem(
              "feature-1",
              "Compelling photography and typography provide a beautiful reading",
              86,
            ),
            _buildFeatureItem(
              "feature-2",
              "Sector news never shares your personal data with advertisers or publishers",
              40,
            ),
            _buildFeatureItem(
              "feature-3",
              "You can get Premium to unlock hundreds of publications",
              40,
            ),
            const Spacer(),
            _buildStartButton()
          ],
        ),
      ),
    );
  }
}

上面的 TextButton 本来运用的是 FlatButton, 但是它已经被弃用了。

相关的效果图:

Flutter 仿写新闻客户端

静态路由,组件抽取,登陆注册页面

为了实现静态路由,咱们来定义下登陆和注册的页面:

  • 登录页 lib/pages/sign_in/sign_in.dart
  • 注册页 lib/pages/sign_up/sign_up.dart
  • 静态路由 lib/routes.dart
// 登陆页面初始化
import 'package:flutter/material.dart';
class SignInPage extends StatelessWidget {
  SignInPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: <Widget>[
            Text('Sign In'),
            Text('Hello'),
            Text('World'),
          ],
        ),
      ),
    );
  }
}
// 注册页面初始化
import 'package:flutter/material.dart';
class SignUpPage extends StatelessWidget {
  SignUpPage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: <Widget>[
            Text('Sign Up'),
            Text('Hello'),
            Text('World'),
          ],
        ),
      ),
    );
  }
}
// 路由信息
import 'package:jimmy_flutter_demo/pages/sign_in/sign_in.dart';
import 'package:jimmy_flutter_demo/pages/sign_up/sign_up.dart';
// 静态路由
var staticRoutes = {
  "/sign-in": (context) => SignInPage(), // 登录
  "/sign-up": (context) => SignUpPage(), // 注册
};

装置运用 fluttertoast 报错的处理:

[Parse Issue (Xcode): Module 'fluttertoast' not found

处理方案:

1.  进入项目 ios 文件夹,删去文件 **"Podfile"** 和 **"Podfile. Lock"** 
2.  ios 目录下,在终端履行`flutter clean`命令行
3.  回到项目根目录,在终端履行`flutter pub get`
4.  ios 目录下,在终端履行`pod install`

组件 appBar 拆分过程的报错:The argument type 'Widget' can't be assigned to the parameter type 'PreferredSizeWidget?'

处理方案:

因为咱们定义了 appBar 组件是 `Widget`,咱们应该定义其为 `PreferredSizeWidget`。
Widget transparentAppBar({
  required BuildContext context,
  required List<Widget> actions,
}) {}
// 改为
PreferredSizeWidget transparentAppBar({
  required BuildContext context,
  required List<Widget> actions,
}) {}

组件抽取

比方: toast

import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:jimmy_flutter_demo/common/utils/utils.dart';
import 'package:fluttertoast/fluttertoast.dart';
Future toastInfo({
  required String msg,
  Color backgroundColor = Colors.black,
  Color textColor = Colors.white,
}) async {
  return await Fluttertoast.showToast(
    msg: msg,
    toastLength: Toast.LENGTH_SHORT,
    gravity: ToastGravity.TOP,
    timeInSecForIosWeb: 1,
    backgroundColor: backgroundColor,
    textColor: textColor,
    fontSize: duSetFontSize(16),
  );
}

又比方:appBar

import 'package:flutter/material.dart';
import 'package:jimmy_flutter_demo/common/values/values.dart';
// 透明布景的 AppBar
PreferredSizeWidget transparentAppBar({
  // 运用 PreferredSizeWidget 定义,而不是 Widget
  required BuildContext context,
  required List<Widget> actions,
}) {
  return AppBar(
    backgroundColor: Colors.transparent,
    elevation: 0,
    title: const Text(''),
    leading: IconButton(
      icon: const Icon(
        Icons.arrow_back,
        color: AppColors.primaryText,
      ),
      onPressed: () {
        Navigator.pop(context);
      },
    ),
    actions: actions,
  );
}

Dio 的封装运用

  1. 处理报错:Non-nullable instance field '_storage' must be initialized.\ Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.

处理方案,在变量 _storage 增加 late 修饰符。

  1. 处理报错 The argument type 'void Function(RequestOptions)' can't be assigned to the parameter type 'void Function(RequestOptions, RequestInterceptorHandler)?' 封装 dio 的时分呈现。

处理方案,能够测验方法如下:

initializeInterceptor(){
    _dio.interceptors.add(InterceptorsWrapper(
        onError: (error, errorInterceptorHandler ){
          print(error.message);
        },
        onRequest: (request, requestInterceptorHandler){
          print("${request.method} | ${request.path}");
        },
        onResponse: (response, responseInterceptorHandler) {
          print('${response.statusCode} ${response.statusCode} ${response.data}');
        }
    ));
  }

后续更新…

往期精彩推荐

  • Dart 知识点 – 数据类型
  • Flutter 开发呈现的那些 Bugs 和处理方案「继续更新… 」

假如读者觉得文章还能够,不防一键三连:重视➕点赞➕收藏