【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-卓伊凡换人优雅草Alex
由于卓伊凡工作实在太多,工作繁忙且卓伊凡每天晚上还要直播,因此本项目已前端部分转交优雅草Alex继续并且更新-为了保证每日更新
优雅草Alex 【高级全栈开发工程师-任职世界500强企业-月薪30k+】接下来就看这位大佬的表演吧
经典案例-在优雅草手撸加速器-win-mac-安卓+苹果 4端【仅用了一个月时间】
上篇我们做了自定义组件,本文继续完善注册相关页面并且实现跳转
闲话不多,开源仓库地址,可以观摩已经写好的代码:
https://gitee.com/youyacao/ff-flutter
https://www.youyacao.cn/freefirend
·更新了getx路由 ·增加了屏幕适配 ·基础导航栏开发处理 ·重建了Android ·布局规划了包含注册,直播,其他等页面框架 ·整体处理
remote: Enumerating objects: 98, done.
remote: Counting objects: 100% (94/94), done.
remote: Compressing objects: 100% (50/50), done.
remote: Total 62 (delta 19), reused 13 (delta 0), pack-reused 0
Unpacking objects: 100% (62/62), 18.63 KiB | 141.00 KiB/s, done.
From https://gitee.com/youyacao/ff-flutter
2d36d20..71239bb master -> origin/master
Updating 2d36d20..71239bb
Fast-forward
.cursorrules | 130 +++++++++++
.fvmrc | 3 +
.gitignore | 3 +
.metadata | 30 +--
.vscode/launch.json | 6 +-
.vscode/settings.json | 3 +
android/.gitignore | 2 +-
android/app/build.gradle | 53 +++--
android/app/src/main/AndroidManifest.xml | 5 +-
.../{freefirend => ff_flutter}/MainActivity.kt | 2 +-
.../app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 1508805 -> 544 bytes
.../app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1508805 -> 442 bytes
.../app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 1508805 -> 721 bytes
.../app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1508805 -> 1031 bytes
.../src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1508805 -> 1443 bytes
android/build.gradle | 4 +-
android/gradle.properties | 2 +-
android/gradle/wrapper/gradle-wrapper.properties | 2 +-
android/settings.gradle | 9 +-
lib/controllers/index_controller.dart | 5 +
lib/controllers/register_controller.dart | 14 ++
lib/controllers/sms_login_controller.dart | 5 +
lib/main.dart | 34 +--
lib/routes/app_pages.dart | 38 +++
lib/routes/app_routes.dart | 5 +
lib/screens/account_screen.dart | 16 ++
lib/screens/home_screen.dart | 254 ++++++++++++++++++++
lib/screens/index.dart | 259 ++++++++-------------
lib/screens/message_screen.dart | 16 ++
lib/screens/register.dart | 109 +++++----
lib/screens/short_video_screen.dart | 16 ++
linux/main.cc | 6 +
linux/my_application.cc | 124 ++++++++++
linux/my_application.h | 18 ++
pubspec.lock | 66 ++++--
pubspec.yaml | 2 +
test/widget_test.dart | 30 +++
37 files changed, 972 insertions(+), 299 deletions(-)
create mode 100644 .cursorrules
create mode 100644 .fvmrc
create mode 100644 .vscode/settings.json
rename android/app/src/main/kotlin/com/example/{freefirend => ff_flutter}/MainActivity.kt (74%)
create mode 100644 lib/controllers/index_controller.dart
create mode 100644 lib/controllers/register_controller.dart
create mode 100644 lib/controllers/sms_login_controller.dart
create mode 100644 lib/routes/app_pages.dart
create mode 100644 lib/routes/app_routes.dart
create mode 100644 lib/screens/account_screen.dart
create mode 100644 lib/screens/home_screen.dart
create mode 100644 lib/screens/message_screen.dart
create mode 100644 lib/screens/short_video_screen.dart
create mode 100644 linux/main.cc
create mode 100644 linux/my_application.cc
create mode 100644 linux/my_application.h
create mode 100644 test/widget_test.dart
先打包个,flutter build apk 运行, 报错
先运行下看看
可以成功运行,那么已经可以确定肯定是gradle 打包问题了,解决打包问题前我们已经看看更新的内容吧,
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:ff_flutter/routes/app_pages.dart';
import 'package:ff_flutter/routes/app_routes.dart';
void main() {
// 初始化日志记录器
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen((record) {
debugPrint('${record.level.name}: ${record.time}: ${record.message}');
});
runApp(const MainApp());
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(750, 1624), // 设置设计稿尺寸
minTextAdapt: true,
splitScreenMode: true,
builder: (context, child) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'freefirend',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: AppRoutes.INDEX,
getPages: AppPages.pages,
);
});
}
}
从入口文件看到 更新了 路由app_routes.dart,更新了要么框架 app_pages.dart
页面框架代码:
import 'package:get/get.dart';
import 'package:ff_flutter/screens/index.dart';
import 'package:ff_flutter/screens/register.dart';
import 'package:ff_flutter/screens/smslogin.dart' as sms;
import 'package:ff_flutter/screens/home_screen.dart';
import 'package:ff_flutter/screens/short_video_screen.dart';
import 'package:ff_flutter/screens/message_screen.dart';
import 'package:ff_flutter/screens/account_screen.dart';
import 'package:ff_flutter/routes/app_routes.dart';
import 'package:ff_flutter/controllers/index_controller.dart';
import 'package:ff_flutter/controllers/register_controller.dart';
import 'package:ff_flutter/controllers/sms_login_controller.dart';
class AppPages {
static final pages = [
GetPage(
name: AppRoutes.INDEX,
page: () => IndexScreen(),
binding: BindingsBuilder(() {
Get.lazyPut(() => IndexController());
}),
),
GetPage(
name: AppRoutes.REGISTER,
page: () => const RegisterScreen(),
binding: BindingsBuilder(() {
Get.lazyPut(() => RegisterController());
}),
),
GetPage(
name: AppRoutes.SMS_LOGIN,
page: () => const sms.SmsLoginScreen(),
binding: BindingsBuilder(() {
Get.lazyPut(() => SmsLoginController());
}),
),
];
}
路由文件代码,
abstract class AppRoutes {
static const INDEX = '/';
static const REGISTER = '/register';
static const SMS_LOGIN = '/sms-login';
}
创建了 control 文件,里面对页面进行了方法的文件创建和默认的内容,诸如注册的调用
import 'package:get/get.dart';
class RegisterController extends GetxController {
final RxBool agreedToTerms = false.obs;
final RxString selectedCountryCode = '+1'.obs;
void updateAgreedToTerms(bool value) {
agreedToTerms.value = value;
}
void updateSelectedCountryCode(String value) {
selectedCountryCode.value = value;
}
}
这段代码定义了一个名为 RegisterController
的类,继承自 GetxController
。它包含两个可观察变量:agreedToTerms
表示用户是否同意条款,默认为 false
;selectedCountryCode
表示选择的国家代码,默认为 +1
。还提供了两个方法用于更新这些变量的值。
mermaid
flowchart TD A[初始化 RegisterController] --> B{更新 agreedToTerms} B -->|调用 updateAgreedToTerms| C[设置 agreedToTerms 值] A --> D{更新 selectedCountryCode} D -->|调用 updateSelectedCountryCode| E[设置 selectedCountryCode 值]
整体主要是对框架进行了适配,此刻我发现同事把我打包图标默认的默认被换了,因此我建立APP打包的图标自定义
找到原图标,下载并保存
修改pubspec.yaml, 加入自定义图标代码
flutter_launcher_icons:
image_path: "assets/icon/logo.png" # 指定用于生成图标的源图像路径
android: true # 表示要为 Android 平台生成图标
ios: true # 表示要为 iOS 平台生成图标
remove_alpha_ios: true # 移除 iOS 图标中的透明通道
在资源目录加入assets\icons中logo.png文件
再来看看index.dart中对导航栏的书写,
import 'package:flutter/material.dart';
import 'package:ff_flutter/screens/register.dart'; // 导入 register.dart 文件
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:ff_flutter/routes/app_routes.dart';
import 'package:ff_flutter/screens/home_screen.dart';
import 'package:ff_flutter/screens/short_video_screen.dart';
import 'package:ff_flutter/screens/message_screen.dart';
import 'package:ff_flutter/screens/account_screen.dart';
class IndexScreen extends StatefulWidget {
// 改为 StatefulWidget
@override
State<IndexScreen> createState() => _IndexScreenState();
}
class _IndexScreenState extends State<IndexScreen> {
int _selectedIndex = 0;
final List<Widget> _pages = [
HomeScreen(),
ShortVideoScreen(),
MessageScreen(),
AccountScreen(),
];
void _onItemTapped(int index) {
setState(() {
_selectedIndex = index;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFF1E1E1E),
body: IndexedStack(
index: _selectedIndex,
children: _pages,
),
bottomNavigationBar: Container(
height: 168.h,
decoration: const BoxDecoration(
color: Color(0xFF1E1E1E),
),
child: Theme(
data: ThemeData(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
),
child: BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
activeIcon: Icon(Icons.home, color: Color(0xFFE7568C)),
),
BottomNavigationBarItem(
icon: Icon(Icons.play_circle_outline),
label: 'Short Video',
activeIcon:
Icon(Icons.play_circle_outline, color: Color(0xFFE7568C)),
),
BottomNavigationBarItem(
icon: Icon(Icons.notifications_none),
label: 'Message',
activeIcon:
Icon(Icons.notifications_none, color: Color(0xFFE7568C)),
),
BottomNavigationBarItem(
icon: Icon(Icons.person_outline),
label: 'Account',
activeIcon:
Icon(Icons.person_outline, color: Color(0xFFE7568C)),
),
],
currentIndex: _selectedIndex,
selectedItemColor: const Color(0xFFE7568C),
unselectedItemColor: Colors.grey,
backgroundColor: const Color(0xFF1E1E1E),
type: BottomNavigationBarType.fixed,
selectedFontSize: 24.sp,
unselectedFontSize: 24.sp,
iconSize: 48.sp,
elevation: 0,
onTap: _onItemTapped,
),
),
),
);
}
}
class DownloadButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton.icon(
onPressed: () {
// 处理下载逻辑
},
icon: const Icon(
Icons.system_update_alt,
size: 30,
color: Color(0xfff1f1f1), // 设置图标颜色为 0xfff1f1f1
),
label: const Text(
"Download",
style: TextStyle(
color: Color(0xfff1f1f1),
fontSize: 26,
fontFamily: "PingFang SC",
fontWeight: FontWeight.w800,
),
),
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0xffe7568c), // 使用 backgroundColor 替代 primary
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(27.r),
),
padding: EdgeInsets.symmetric(horizontal: 17.w, vertical: 9.h),
minimumSize: Size(195.w, 54.h),
),
);
}
}
class CustomIconButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 54.w,
height: 54.h,
child: Stack(
children: [
Container(
width: 54.w,
height: 54.h,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Color(0xff151313),
),
),
Positioned.fill(
child: Align(
alignment: Alignment.center,
child: const Icon(
Icons.notifications,
size: 36,
color: Color(0xfff1f1f1), // 设置图标颜色为 0xfff1f1f1
),
),
),
],
),
);
}
}
class MoreButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
"more",
style: TextStyle(
color: Color(0xff929292),
fontSize: 26,
),
),
SizedBox(width: 10.w),
Transform.rotate(
angle: 3.14,
child: Container(
width: 26.w,
height: 26.h,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.r),
),
child: const Icon(
Icons.arrow_back, // 替换为合适的图标
size: 26,
color: Color(0xfff1f1f1), // 设置图标颜色为 0xfff1f1f1
),
),
),
],
);
}
}
结束,最后我们再来看看打包问题,解决之前的报错 只需要对Gradle处理,以下是常用处理方式,gradlew clean解决了我们的问题
,java.net.SocketException: Unexpected end of file from server
错误通常发生在 Gradle 尝试从服务器下载依赖项时遇到连接问题。以下是一些可能的解决方案:
gradlew clean
清理构建缓存。.gradle
文件夹(位于用户主目录下)以清除全局 Gradle 缓存。build.gradle
文件中的 distributionUrl
到最新的 Gradle 版本。pubspec.yaml
中的所有依赖项都能正常访问。flutter pub get
命令来获取 Dart/Flutter 依赖项。gradle.properties
文件中添加 org.gradle.offline=true
来启用 Gradle 离线模式。但是又遇到了新的问题,
问题出在 Gradle 版本与 Java 版本不兼容。具体来说,Gradle 不支持当前使用的 Java 版本(Java 11 或更高版本)。以下是解决此问题的步骤
flutter doctor --verbose
检查当前使用的 Java 和 Gradle 版本。android/gradle/wrapper/gradle-wrapper.properties
文件。distributionUrl
以使用一个与当前 Java 版本兼容的 Gradle 版本。例如:properties
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
File -> Project Structure -> SDK Location
。JDK location
到一个兼容的 JDK 版本。bash
cd android ./gradlew clean
bash
flutter clean flutter pub get flutter build apk --release
最后我们成功解决问题,已经提交打包,发现,还是不兼容,这时候怎么办,我们需要用到 FVM (Flutter Version Manager)
由于错误发生在 flutter_tools
中,你还需要确保 Flutter 工具本身使用的 Gradle 版本是最新的。你可以通过以下步骤来更新 Flutter 工具的 Gradle 版本:
packages/flutter_tools/gradle/gradle-wrapper.properties
文件。distributionUrl
为最新的 Gradle 版本,例如:properties
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
如果你使用多个 Flutter 版本,建议使用 FVM 来管理不同项目的 Flutter 版本,确保每个项目使用兼容的 Flutter 和 Gradle 版本。
通过以上步骤,你应该能够解决 Gradle 版本与 Java 版本不兼容的问题,并成功构建你的 Flutter 项目。如果问题仍然存在,请提供更多的日志输出(如 --stacktrace
或 --info
),以便进一步诊断。
使用 FVM(Flutter Version Manager)可以帮助你管理不同项目的 Flutter 版本,确保每个项目使用兼容的 Flutter 和 Gradle 版本。以下是详细的步骤来安装和配置 FVM:
bash
dart pub global activate fvm
~/.pub-cache/bin
。你可以通过以下命令将其添加到当前终端会话中:bash
export PATH="$PATH":"$HOME/.pub-cache/bin"
如果你想永久添加,请将上述命令添加到你的 shell 配置文件中(例如 .bashrc
、.zshrc
或 .bash_profile
)。
bash
fvm install
fvm.yaml
文件中读取指定的 Flutter 版本。如果没有该文件,FVM 会提示你选择一个版本并创建 fvm.yaml
文件。你也可以手动编辑 fvm.yaml
文件来指定版本:yaml
flutter_sdk: version: "3.7.12" # 替换为你需要的版本号
bash
fvm flutter --version
bash
fvm flutter pub get
bash
cd android ../.fvm/flutter_sdk/bin/flutter clean ../.fvm/flutter_sdk/bin/flutter pub get ../.fvm/flutter_sdk/bin/flutter build apk --release
android/gradle/wrapper/gradle-wrapper.properties
文件,修改 distributionUrl
以使用一个与当前 Java 版本兼容的 Gradle 版本。例如:properties
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
File -> Project Structure -> SDK Location
。JDK location
到一个兼容的 JDK 版本。确保你的环境变量配置正确,特别是 JAVA_HOME
和 PATH
变量,指向正确的 JDK 和 Gradle 安装路径。
假设你已经安装了 FVM 并将其添加到 PATH 中,以下是完整的示例流程:
bash
cd g:\clone\ff-flutter fvm install
bash
fvm flutter --version
bash
fvm flutter pub get
bash
cd android ../.fvm/flutter_sdk/bin/flutter clean ../.fvm/flutter_sdk/bin/flutter pub get ../.fvm/flutter_sdk/bin/flutter build apk --release
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。