新建项目,加入图片字体,编写欢迎界面
新建项目
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
编写欢迎页面
添加屏幕适配的包。
代码语言:javascript复制 # 屏幕适配
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
做全局的引入。
然后对欢迎页面进行添加,内容如下:
代码语言:javascript复制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, 但是它已经被弃用了。
相关的效果图:
静态路由,组件抽取,登陆注册页面
为了实现静态路由,我们来定义下登陆和注册的页面:
- 登录页 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'),
],
),
),
);
}
}
代码语言:javascript复制// 注册页面初始化
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'),
],
),
),
);
}
}
代码语言:javascript复制// 路由信息
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
解决方案:
代码语言:javascript复制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?'
**
解决方案:
代码语言:javascript复制因为我们定义了 appBar 组件是 `Widget`,我们应该定义其为 `PreferredSizeWidget`。
Widget transparentAppBar({
required BuildContext context,
required List<Widget> actions,
}) {}
// 改为
PreferredSizeWidget transparentAppBar({
required BuildContext context,
required List<Widget> actions,
}) {}
组件抽取
比如: toast
代码语言:javascript复制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
代码语言:javascript复制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 的封装使用
- 处理报错:
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
修饰符。
- 处理报错
The argument type 'void Function(RequestOptions)' can't be assigned to the parameter type 'void Function(RequestOptions, RequestInterceptorHandler)?'
封装dio
的时候出现。
解决方案,可以尝试方法如下:
代码语言:javascript复制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 和解决方案「持续更新... 」
如果读者觉得文章还可以,不防一键三连:关注➕点赞➕收藏