mod: 图标&样式&网页登录
@ -47,11 +47,11 @@ android {
|
||||
applicationId "com.example.pilipala"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||
minSdkVersion flutter.minSdkVersion
|
||||
// minSdkVersion flutter.minSdkVersion
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
minSdkVersion 17
|
||||
minSdkVersion 19
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 13 KiB |
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 5.5 KiB |
4
android/app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#ffffff</color>
|
||||
</resources>
|
BIN
assets/images/logo/logo_android.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
assets/images/logo/logo_ios.png
Normal file
After Width: | Height: | Size: 13 KiB |
@ -31,6 +31,10 @@ PODS:
|
||||
- FMDB (>= 2.7.5)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- webview_cookie_manager (0.0.1):
|
||||
- Flutter
|
||||
- webview_flutter_wkwebview (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
@ -43,6 +47,8 @@ DEPENDENCIES:
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- webview_cookie_manager (from `.symlinks/plugins/webview_cookie_manager/ios`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
@ -71,6 +77,10 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/sqflite/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
webview_cookie_manager:
|
||||
:path: ".symlinks/plugins/webview_cookie_manager/ios"
|
||||
webview_flutter_wkwebview:
|
||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
||||
@ -86,6 +96,8 @@ SPEC CHECKSUMS:
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7
|
||||
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
|
||||
|
||||
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 309 B |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 660 B |
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 443 B |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 660 B |
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 858 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1020 B |
After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 3.3 KiB |
@ -5,18 +5,22 @@ class Api {
|
||||
static const String hotList = '/x/web-interface/popular';
|
||||
// 视频详情
|
||||
// 竖屏 https://api.bilibili.com/x/web-interface/view?aid=527403921
|
||||
// https://api.bilibili.com/x/web-interface/view/detail 获取视频超详细信息(web端)
|
||||
static const String videoIntro = '/x/web-interface/view';
|
||||
|
||||
// 视频详情页 相关视频
|
||||
static const String relatedList = '/x/web-interface/archive/related';
|
||||
|
||||
// 用户(被)关注数、投稿数
|
||||
// https://api.bilibili.com/x/relation/stat?vmid=697166795
|
||||
static const String userStat = '/x/relation/stat';
|
||||
|
||||
// 评论列表
|
||||
static const String replyList = '/x/v2/reply';
|
||||
|
||||
// 楼中楼
|
||||
static const String replyReplyList = '/x/v2/reply/reply';
|
||||
|
||||
// 用户(被)关注数、投稿数
|
||||
// https://api.bilibili.com/x/relation/stat?vmid=697166795
|
||||
static const String userStat = '/x/relation/stat';
|
||||
|
||||
// 获取用户信息
|
||||
static const String userInfo = '/x/web-interface/nav';
|
||||
}
|
||||
|
@ -10,4 +10,13 @@ class UserHttp {
|
||||
return {'status': false};
|
||||
}
|
||||
}
|
||||
|
||||
static Future<dynamic> userInfo() async {
|
||||
var res = await Request().get(Api.userInfo);
|
||||
if (res.data['code'] == 0) {
|
||||
return {'status': true, 'data': res.data['data']};
|
||||
} else {
|
||||
return {'status': false};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
@ -23,18 +24,24 @@ class MyApp extends StatelessWidget {
|
||||
return GetMaterialApp(
|
||||
title: 'PiLiPaLa',
|
||||
theme: ThemeData(
|
||||
colorScheme: lightDynamic ??
|
||||
ColorScheme.fromSeed(
|
||||
seedColor: Colors.green, brightness: Brightness.light),
|
||||
useMaterial3: true),
|
||||
colorScheme: lightDynamic ??
|
||||
ColorScheme.fromSeed(
|
||||
seedColor: Colors.green,
|
||||
brightness: Brightness.light,
|
||||
),
|
||||
useMaterial3: true,
|
||||
),
|
||||
darkTheme: ThemeData(
|
||||
colorScheme: darkDynamic ??
|
||||
ColorScheme.fromSeed(
|
||||
seedColor: Colors.green, brightness: Brightness.dark),
|
||||
useMaterial3: true),
|
||||
colorScheme: darkDynamic ??
|
||||
ColorScheme.fromSeed(
|
||||
seedColor: Colors.green,
|
||||
brightness: Brightness.dark,
|
||||
),
|
||||
useMaterial3: true,
|
||||
),
|
||||
getPages: Routes.getPages,
|
||||
home: const MainApp(),
|
||||
// home: const Scaffold(),
|
||||
builder: FlutterSmartDialog.init(),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
7
lib/models/user/info.dart
Normal file
@ -0,0 +1,7 @@
|
||||
class UserInfoData {
|
||||
UserInfoData({
|
||||
this.isLogin,
|
||||
});
|
||||
|
||||
bool? isLogin;
|
||||
}
|
@ -30,7 +30,7 @@ class HomeAppBar extends StatelessWidget {
|
||||
title: const Text(
|
||||
'PiLiPaLa',
|
||||
style: TextStyle(
|
||||
fontSize: 19,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
letterSpacing: 1,
|
||||
fontFamily: 'ArchivoNarrow',
|
||||
|
@ -0,0 +1,14 @@
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/user.dart';
|
||||
|
||||
class MineController extends GetxController {
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// queryUserInfo();
|
||||
}
|
||||
|
||||
Future queryUserInfo() async {
|
||||
var res = await UserHttp.userInfo();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/constants.dart';
|
||||
import 'controller.dart';
|
||||
|
||||
class MinePage extends StatefulWidget {
|
||||
const MinePage({super.key});
|
||||
@ -10,6 +12,8 @@ class MinePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MinePageState extends State<MinePage> {
|
||||
final MineController _mineController = Get.put(MineController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -30,136 +34,161 @@ class _MinePageState extends State<MinePage> {
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () async {
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const SizedBox(width: 20),
|
||||
ClipOval(
|
||||
child: Container(
|
||||
width: 75,
|
||||
height: 75,
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
child: Center(
|
||||
child: Image.asset('assets/images/loading.png'),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
'/webview',
|
||||
parameters: {
|
||||
'url':
|
||||
'https://passport.bilibili.com/h5-app/passport/login',
|
||||
'type': 'login',
|
||||
'pageTitle': '登录bilibili',
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 10, bottom: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 20),
|
||||
ClipOval(
|
||||
child: Container(
|
||||
width: 75,
|
||||
height: 75,
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
child: Center(
|
||||
child: Image.asset('assets/images/loading.png'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Text(
|
||||
'点击登录',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Text(
|
||||
'点击登录',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
TextStyle style = TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.titleMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.bold);
|
||||
return SizedBox(
|
||||
height: constraints.maxWidth / 3 * 0.6,
|
||||
child: GridView.count(
|
||||
primary: false,
|
||||
padding: const EdgeInsets.all(0),
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 1.67,
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
borderRadius: StyleString.mdRadius,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('-', style: style),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'动态',
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
borderRadius: StyleString.mdRadius,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'50',
|
||||
style: style,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'关注',
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
borderRadius: StyleString.mdRadius,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'-',
|
||||
style: style,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'粉丝',
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
TextStyle style = TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.titleMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontWeight: FontWeight.bold);
|
||||
return SizedBox(
|
||||
height: constraints.maxWidth / 3 * 0.6,
|
||||
child: GridView.count(
|
||||
primary: false,
|
||||
padding: const EdgeInsets.all(0),
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 1.67,
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
borderRadius: StyleString.mdRadius,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('-', style: style),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'动态',
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SizedBox(
|
||||
height: constraints.maxWidth / 4 * 0.8,
|
||||
child: GridView.count(
|
||||
primary: false,
|
||||
padding: const EdgeInsets.all(0),
|
||||
crossAxisCount: 4,
|
||||
childAspectRatio: 1.25,
|
||||
children: <Widget>[
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.download),
|
||||
onTap: () => {},
|
||||
text: '离线缓存',
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
borderRadius: StyleString.mdRadius,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'50',
|
||||
style: style,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'关注',
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
],
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.clockRotateLeft),
|
||||
onTap: () => {},
|
||||
text: '历史记录',
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
borderRadius: StyleString.mdRadius,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'-',
|
||||
style: style,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'粉丝',
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
],
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.star),
|
||||
onTap: () => {},
|
||||
text: '我的收藏',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SizedBox(
|
||||
height: constraints.maxWidth / 4 * 0.8,
|
||||
child: GridView.count(
|
||||
primary: false,
|
||||
padding: const EdgeInsets.all(0),
|
||||
crossAxisCount: 4,
|
||||
childAspectRatio: 1.25,
|
||||
children: <Widget>[
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.download),
|
||||
onTap: () => {},
|
||||
text: '离线缓存',
|
||||
),
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.clockRotateLeft),
|
||||
onTap: () => {},
|
||||
text: '历史记录',
|
||||
),
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.star),
|
||||
onTap: () => {},
|
||||
text: '我的收藏',
|
||||
),
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.film),
|
||||
onTap: () => {},
|
||||
text: '稍后再看',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
ActionItem(
|
||||
icon: const Icon(FontAwesomeIcons.film),
|
||||
onTap: () => {},
|
||||
text: '稍后再看',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -188,7 +217,10 @@ class ActionItem extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon!.icon!),
|
||||
Icon(
|
||||
icon!.icon!,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
text!,
|
||||
|
@ -247,7 +247,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
children: const [
|
||||
Icon(
|
||||
FontAwesomeIcons.lemon,
|
||||
size: 17,
|
||||
size: 15,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text('关注'),
|
||||
@ -334,10 +334,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
||||
? widget.videoDetail!.stat!.coin!.toString()
|
||||
: '-'),
|
||||
ActionItem(
|
||||
icon: const Icon(
|
||||
FontAwesomeIcons.heart,
|
||||
size: 17,
|
||||
),
|
||||
icon: const Icon(FontAwesomeIcons.star),
|
||||
onTap: () => {},
|
||||
selectStatus: false,
|
||||
loadingStatus: widget.loadingStatus,
|
||||
@ -386,10 +383,11 @@ class ActionItem extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(icon!.icon!,
|
||||
size: 20,
|
||||
color: selectStatus
|
||||
? Theme.of(context).primaryColor
|
||||
: Theme.of(context).colorScheme.outline),
|
||||
const SizedBox(height: 2),
|
||||
const SizedBox(height: 4),
|
||||
AnimatedOpacity(
|
||||
opacity: loadingStatus! ? 0 : 1,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
|
@ -164,7 +164,7 @@ class ReplyItem extends StatelessWidget {
|
||||
TextSpan(
|
||||
children: [
|
||||
if (replyItem!.isTop!)
|
||||
WidgetSpan(child: UpTag(tagText: '置顶')),
|
||||
WidgetSpan(child: UpTag(tagText: 'TOP')),
|
||||
buildContent(context, replyItem!.content!),
|
||||
],
|
||||
),
|
||||
@ -200,6 +200,15 @@ class ReplyItem extends StatelessWidget {
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
if (replyItem!.replyControl != null &&
|
||||
replyItem!.replyControl!.location != null)
|
||||
Text(
|
||||
' • ${replyItem!.replyControl!.location!}',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
const Spacer(),
|
||||
if (replyControl!.isUpTop!)
|
||||
Icon(Icons.favorite, color: Colors.red[400], size: 18),
|
||||
@ -209,7 +218,7 @@ class ReplyItem extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.thumb_up_alt_outlined,
|
||||
FontAwesomeIcons.thumbsUp,
|
||||
size: 16,
|
||||
color: color,
|
||||
),
|
||||
|
@ -74,7 +74,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
||||
body: Column(
|
||||
children: [
|
||||
Container(
|
||||
height: 50,
|
||||
height: 45,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
@ -91,7 +91,6 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
||||
margin: const EdgeInsets.only(left: 20),
|
||||
child: Obx(
|
||||
() => TabBar(
|
||||
splashBorderRadius: BorderRadius.circular(6),
|
||||
dividerColor: Colors.transparent,
|
||||
tabs: videoDetailController.tabs
|
||||
.map((String name) => Tab(text: name))
|
||||
|
69
lib/pages/webview/controller.dart
Normal file
@ -0,0 +1,69 @@
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/http/constants.dart';
|
||||
import 'package:pilipala/http/user.dart';
|
||||
import 'package:pilipala/utils/cookie.dart';
|
||||
import 'package:webview_cookie_manager/webview_cookie_manager.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class WebviewController extends GetxController {
|
||||
String url = '';
|
||||
String type = '';
|
||||
String pageTitle = '';
|
||||
final WebViewController controller = WebViewController();
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
url = Get.parameters['url']!;
|
||||
type = Get.parameters['type']!;
|
||||
pageTitle = Get.parameters['pageTitle']!;
|
||||
|
||||
webviewInit();
|
||||
}
|
||||
|
||||
webviewInit() {
|
||||
controller
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
// 页面加载
|
||||
onProgress: (int progress) {
|
||||
// Update loading bar.
|
||||
},
|
||||
onPageStarted: (String url) {},
|
||||
// 加载完成
|
||||
onPageFinished: (String url) async {
|
||||
if (url.startsWith(
|
||||
'https://passport.bilibili.com/web/sso/exchange_cookie') ||
|
||||
url.startsWith('https://m.bilibili.com/')) {
|
||||
try {
|
||||
var cookies =
|
||||
await WebviewCookieManager().getCookies(HttpString.baseUrl);
|
||||
var apiCookies =
|
||||
await WebviewCookieManager().getCookies(HttpString.baseUrl);
|
||||
await SetCookie.onSet(cookies, HttpString.baseUrl);
|
||||
await SetCookie.onSet(apiCookies, HttpString.baseApiUrl);
|
||||
var result = await UserHttp.userInfo();
|
||||
bool isLogin = result['data']['isLogin'];
|
||||
if (isLogin) {
|
||||
SmartDialog.showToast('登录成功');
|
||||
Get.back();
|
||||
}
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
onWebResourceError: (WebResourceError error) {},
|
||||
onNavigationRequest: (NavigationRequest request) {
|
||||
if (request.url.startsWith('https://www.youtube.com/')) {
|
||||
return NavigationDecision.prevent;
|
||||
}
|
||||
return NavigationDecision.navigate;
|
||||
},
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse(url));
|
||||
}
|
||||
}
|
4
lib/pages/webview/index.dart
Normal file
@ -0,0 +1,4 @@
|
||||
library webview;
|
||||
|
||||
export './controller.dart';
|
||||
export './view.dart';
|
29
lib/pages/webview/view.dart
Normal file
@ -0,0 +1,29 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'controller.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class WebviewPage extends StatefulWidget {
|
||||
const WebviewPage({super.key});
|
||||
|
||||
@override
|
||||
State<WebviewPage> createState() => _WebviewPageState();
|
||||
}
|
||||
|
||||
class _WebviewPageState extends State<WebviewPage> {
|
||||
final WebviewController _webviewController = Get.put(WebviewController());
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
centerTitle: false,
|
||||
title: Text(
|
||||
_webviewController.pageTitle,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
body: WebViewWidget(controller: _webviewController.controller),
|
||||
);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ import 'package:pilipala/pages/home/index.dart';
|
||||
import 'package:pilipala/pages/hot/index.dart';
|
||||
import 'package:pilipala/pages/preview/index.dart';
|
||||
import 'package:pilipala/pages/video/detail/index.dart';
|
||||
import 'package:pilipala/pages/webview/index.dart';
|
||||
|
||||
class Routes {
|
||||
static final List<GetPage> getPages = [
|
||||
@ -13,6 +14,8 @@ class Routes {
|
||||
// 视频详情
|
||||
GetPage(name: '/video', page: () => const VideoDetailPage()),
|
||||
// 图片预览
|
||||
GetPage(name: '/preview', page: () => const ImagePreview())
|
||||
GetPage(name: '/preview', page: () => const ImagePreview()),
|
||||
//
|
||||
GetPage(name: '/webview', page: () => const WebviewPage())
|
||||
];
|
||||
}
|
||||
|
27
lib/utils/cookie.dart
Normal file
@ -0,0 +1,27 @@
|
||||
import 'dart:io';
|
||||
import 'package:cookie_jar/cookie_jar.dart';
|
||||
import 'package:pilipala/http/init.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||
|
||||
class SetCookie {
|
||||
static onSet(List cookiesList, String url) async {
|
||||
// domain url
|
||||
List<Cookie> jarCookies = [];
|
||||
if (cookiesList.isNotEmpty) {
|
||||
for (var i in cookiesList) {
|
||||
Cookie jarCookie = Cookie(i.name, i.value);
|
||||
jarCookies.add(jarCookie);
|
||||
}
|
||||
}
|
||||
String cookiePath = await Utils.getCookiePath();
|
||||
PersistCookieJar cookieJar = PersistCookieJar(
|
||||
ignoreExpires: true,
|
||||
storage: FileStorage(cookiePath),
|
||||
);
|
||||
await cookieJar.saveFromResponse(Uri.parse(url), jarCookies);
|
||||
// 重新设置 cookie
|
||||
Request.setCookie();
|
||||
return true;
|
||||
}
|
||||
}
|
48
pubspec.lock
@ -262,6 +262,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
flutter_smart_dialog:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_smart_dialog
|
||||
sha256: da7ed8fe78e301e3c2cdaa533d13ed3edcf1853c1ba1a7383b481739569f7b2a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0+6"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@ -725,6 +733,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3"
|
||||
webview_cookie_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: webview_cookie_manager
|
||||
sha256: "425a9feac5cd2cb62a71da3dda5ac2eaf9ece5481ee8d79f3868dc5ba8223ad3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.6"
|
||||
webview_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: webview_flutter
|
||||
sha256: "1a37bdbaaf5fbe09ad8579ab09ecfd473284ce482f900b5aea27cf834386a567"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
webview_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_android
|
||||
sha256: "1acea8def62592123e2fbbca164ed8681a98a890bdcbb88f916d5b4a22687759"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.7.0"
|
||||
webview_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_platform_interface
|
||||
sha256: "78715dc442b7849dbde74e92bb67de1cecf5addf95531c5fb474e72f5fe9a507"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
webview_flutter_wkwebview:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webview_flutter_wkwebview
|
||||
sha256: "61f33512810bf1ee9ac89761a4b02663ff64e8227b7dc80654642acd660fd49d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.2"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
19
pubspec.yaml
@ -63,9 +63,13 @@ dependencies:
|
||||
# webView
|
||||
url_launcher: ^6.1.9
|
||||
flutter_inappwebview: 5.4.4
|
||||
webview_cookie_manager: ^2.0.6
|
||||
webview_flutter: ^4.2.0
|
||||
|
||||
extended_nested_scroll_view: ^6.0.0
|
||||
font_awesome_flutter: ^10.4.0
|
||||
# toast
|
||||
flutter_smart_dialog: ^4.9.0+6
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@ -77,6 +81,21 @@ dev_dependencies:
|
||||
# package. See that file for information about deactivating specific lint
|
||||
# rules and activating additional ones.
|
||||
flutter_lints: ^2.0.0
|
||||
# flutter_launcher_icons:
|
||||
# git:
|
||||
# url: https://github.com/nvi9/flutter_launcher_icons.git
|
||||
# ref: e045d40
|
||||
|
||||
flutter_icons:
|
||||
android: true
|
||||
ios: true
|
||||
remove_alpha_ios: false
|
||||
image_path: assets/images/logo/logo_android.png
|
||||
image_path_android: assets/images/logo/logo_android.png
|
||||
image_path_ios: assets/images/logo/logo_ios.png
|
||||
adaptive_icon_background: "#ffffff"
|
||||
adaptive_icon_foreground: assets/images/logo/logo_android.png
|
||||
adaptive_icon_monochrome: assets/images/logo/logo_android.png
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|