mod: 图标&样式&网页登录
@ -47,11 +47,11 @@ android {
|
|||||||
applicationId "com.example.pilipala"
|
applicationId "com.example.pilipala"
|
||||||
// You can update the following values to match your application needs.
|
// 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.
|
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
|
||||||
minSdkVersion flutter.minSdkVersion
|
// minSdkVersion flutter.minSdkVersion
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion flutter.targetSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
minSdkVersion 17
|
minSdkVersion 19
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
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)
|
- FMDB (>= 2.7.5)
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- webview_cookie_manager (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- webview_flutter_wkwebview (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||||
@ -43,6 +47,8 @@ DEPENDENCIES:
|
|||||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/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:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
@ -71,6 +77,10 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/sqflite/ios"
|
:path: ".symlinks/plugins/sqflite/ios"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/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:
|
SPEC CHECKSUMS:
|
||||||
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
connectivity_plus: 413a8857dd5d9f1c399a39130850d02fe0feaf7e
|
||||||
@ -86,6 +96,8 @@ SPEC CHECKSUMS:
|
|||||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||||
|
webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7
|
||||||
|
webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a
|
||||||
|
|
||||||
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
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';
|
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?aid=527403921
|
||||||
|
// https://api.bilibili.com/x/web-interface/view/detail 获取视频超详细信息(web端)
|
||||||
static const String videoIntro = '/x/web-interface/view';
|
static const String videoIntro = '/x/web-interface/view';
|
||||||
|
|
||||||
// 视频详情页 相关视频
|
// 视频详情页 相关视频
|
||||||
static const String relatedList = '/x/web-interface/archive/related';
|
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 replyList = '/x/v2/reply';
|
||||||
|
|
||||||
// 楼中楼
|
// 楼中楼
|
||||||
static const String replyReplyList = '/x/v2/reply/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};
|
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:get/get.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
@ -23,18 +24,24 @@ class MyApp extends StatelessWidget {
|
|||||||
return GetMaterialApp(
|
return GetMaterialApp(
|
||||||
title: 'PiLiPaLa',
|
title: 'PiLiPaLa',
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
colorScheme: lightDynamic ??
|
colorScheme: lightDynamic ??
|
||||||
ColorScheme.fromSeed(
|
ColorScheme.fromSeed(
|
||||||
seedColor: Colors.green, brightness: Brightness.light),
|
seedColor: Colors.green,
|
||||||
useMaterial3: true),
|
brightness: Brightness.light,
|
||||||
|
),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
darkTheme: ThemeData(
|
darkTheme: ThemeData(
|
||||||
colorScheme: darkDynamic ??
|
colorScheme: darkDynamic ??
|
||||||
ColorScheme.fromSeed(
|
ColorScheme.fromSeed(
|
||||||
seedColor: Colors.green, brightness: Brightness.dark),
|
seedColor: Colors.green,
|
||||||
useMaterial3: true),
|
brightness: Brightness.dark,
|
||||||
|
),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
getPages: Routes.getPages,
|
getPages: Routes.getPages,
|
||||||
home: const MainApp(),
|
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(
|
title: const Text(
|
||||||
'PiLiPaLa',
|
'PiLiPaLa',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 19,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
letterSpacing: 1,
|
letterSpacing: 1,
|
||||||
fontFamily: 'ArchivoNarrow',
|
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:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/common/constants.dart';
|
import 'package:pilipala/common/constants.dart';
|
||||||
|
import 'controller.dart';
|
||||||
|
|
||||||
class MinePage extends StatefulWidget {
|
class MinePage extends StatefulWidget {
|
||||||
const MinePage({super.key});
|
const MinePage({super.key});
|
||||||
@ -10,6 +12,8 @@ class MinePage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MinePageState extends State<MinePage> {
|
class _MinePageState extends State<MinePage> {
|
||||||
|
final MineController _mineController = Get.put(MineController());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -30,136 +34,161 @@ class _MinePageState extends State<MinePage> {
|
|||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: RefreshIndicator(
|
||||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
onRefresh: () async {
|
||||||
|
await Future.delayed(Duration(seconds: 2));
|
||||||
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
InkWell(
|
||||||
children: [
|
onTap: () {
|
||||||
const SizedBox(width: 20),
|
Get.toNamed(
|
||||||
ClipOval(
|
'/webview',
|
||||||
child: Container(
|
parameters: {
|
||||||
width: 75,
|
'url':
|
||||||
height: 75,
|
'https://passport.bilibili.com/h5-app/passport/login',
|
||||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
'type': 'login',
|
||||||
child: Center(
|
'pageTitle': '登录bilibili',
|
||||||
child: Image.asset('assets/images/loading.png'),
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
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(
|
),
|
||||||
'点击登录',
|
const SizedBox(height: 10),
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
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),
|
const SizedBox(height: 20),
|
||||||
LayoutBuilder(
|
Padding(
|
||||||
builder: (context, constraints) {
|
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||||
TextStyle style = TextStyle(
|
child: LayoutBuilder(
|
||||||
fontSize: Theme.of(context).textTheme.titleMedium!.fontSize,
|
builder: (context, constraints) {
|
||||||
color: Theme.of(context).colorScheme.primary,
|
return SizedBox(
|
||||||
fontWeight: FontWeight.bold);
|
height: constraints.maxWidth / 4 * 0.8,
|
||||||
return SizedBox(
|
child: GridView.count(
|
||||||
height: constraints.maxWidth / 3 * 0.6,
|
primary: false,
|
||||||
child: GridView.count(
|
padding: const EdgeInsets.all(0),
|
||||||
primary: false,
|
crossAxisCount: 4,
|
||||||
padding: const EdgeInsets.all(0),
|
childAspectRatio: 1.25,
|
||||||
crossAxisCount: 3,
|
children: <Widget>[
|
||||||
childAspectRatio: 1.67,
|
ActionItem(
|
||||||
children: <Widget>[
|
icon: const Icon(FontAwesomeIcons.download),
|
||||||
InkWell(
|
onTap: () => {},
|
||||||
onTap: () {},
|
text: '离线缓存',
|
||||||
borderRadius: StyleString.mdRadius,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text('-', style: style),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
'动态',
|
|
||||||
style: Theme.of(context).textTheme.labelMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
ActionItem(
|
||||||
InkWell(
|
icon: const Icon(FontAwesomeIcons.clockRotateLeft),
|
||||||
onTap: () {},
|
onTap: () => {},
|
||||||
borderRadius: StyleString.mdRadius,
|
text: '历史记录',
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'50',
|
|
||||||
style: style,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
'关注',
|
|
||||||
style: Theme.of(context).textTheme.labelMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
ActionItem(
|
||||||
InkWell(
|
icon: const Icon(FontAwesomeIcons.star),
|
||||||
onTap: () {},
|
onTap: () => {},
|
||||||
borderRadius: StyleString.mdRadius,
|
text: '我的收藏',
|
||||||
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.film),
|
||||||
),
|
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: '稍后再看',
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -188,7 +217,10 @@ class ActionItem extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(icon!.icon!),
|
Icon(
|
||||||
|
icon!.icon!,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
text!,
|
text!,
|
||||||
|
@ -247,7 +247,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
children: const [
|
children: const [
|
||||||
Icon(
|
Icon(
|
||||||
FontAwesomeIcons.lemon,
|
FontAwesomeIcons.lemon,
|
||||||
size: 17,
|
size: 15,
|
||||||
),
|
),
|
||||||
SizedBox(width: 4),
|
SizedBox(width: 4),
|
||||||
Text('关注'),
|
Text('关注'),
|
||||||
@ -334,10 +334,7 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
|
|||||||
? widget.videoDetail!.stat!.coin!.toString()
|
? widget.videoDetail!.stat!.coin!.toString()
|
||||||
: '-'),
|
: '-'),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
icon: const Icon(
|
icon: const Icon(FontAwesomeIcons.star),
|
||||||
FontAwesomeIcons.heart,
|
|
||||||
size: 17,
|
|
||||||
),
|
|
||||||
onTap: () => {},
|
onTap: () => {},
|
||||||
selectStatus: false,
|
selectStatus: false,
|
||||||
loadingStatus: widget.loadingStatus,
|
loadingStatus: widget.loadingStatus,
|
||||||
@ -386,10 +383,11 @@ class ActionItem extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(icon!.icon!,
|
Icon(icon!.icon!,
|
||||||
|
size: 20,
|
||||||
color: selectStatus
|
color: selectStatus
|
||||||
? Theme.of(context).primaryColor
|
? Theme.of(context).primaryColor
|
||||||
: Theme.of(context).colorScheme.outline),
|
: Theme.of(context).colorScheme.outline),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 4),
|
||||||
AnimatedOpacity(
|
AnimatedOpacity(
|
||||||
opacity: loadingStatus! ? 0 : 1,
|
opacity: loadingStatus! ? 0 : 1,
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
|
@ -164,7 +164,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
TextSpan(
|
TextSpan(
|
||||||
children: [
|
children: [
|
||||||
if (replyItem!.isTop!)
|
if (replyItem!.isTop!)
|
||||||
WidgetSpan(child: UpTag(tagText: '置顶')),
|
WidgetSpan(child: UpTag(tagText: 'TOP')),
|
||||||
buildContent(context, replyItem!.content!),
|
buildContent(context, replyItem!.content!),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -200,6 +200,15 @@ class ReplyItem extends StatelessWidget {
|
|||||||
.labelMedium!
|
.labelMedium!
|
||||||
.copyWith(color: Theme.of(context).colorScheme.outline),
|
.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(),
|
const Spacer(),
|
||||||
if (replyControl!.isUpTop!)
|
if (replyControl!.isUpTop!)
|
||||||
Icon(Icons.favorite, color: Colors.red[400], size: 18),
|
Icon(Icons.favorite, color: Colors.red[400], size: 18),
|
||||||
@ -209,7 +218,7 @@ class ReplyItem extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
Icons.thumb_up_alt_outlined,
|
FontAwesomeIcons.thumbsUp,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
),
|
||||||
|
@ -74,7 +74,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
height: 50,
|
height: 45,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(
|
border: Border(
|
||||||
bottom: BorderSide(
|
bottom: BorderSide(
|
||||||
@ -91,7 +91,6 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
|
|||||||
margin: const EdgeInsets.only(left: 20),
|
margin: const EdgeInsets.only(left: 20),
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => TabBar(
|
() => TabBar(
|
||||||
splashBorderRadius: BorderRadius.circular(6),
|
|
||||||
dividerColor: Colors.transparent,
|
dividerColor: Colors.transparent,
|
||||||
tabs: videoDetailController.tabs
|
tabs: videoDetailController.tabs
|
||||||
.map((String name) => Tab(text: name))
|
.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/hot/index.dart';
|
||||||
import 'package:pilipala/pages/preview/index.dart';
|
import 'package:pilipala/pages/preview/index.dart';
|
||||||
import 'package:pilipala/pages/video/detail/index.dart';
|
import 'package:pilipala/pages/video/detail/index.dart';
|
||||||
|
import 'package:pilipala/pages/webview/index.dart';
|
||||||
|
|
||||||
class Routes {
|
class Routes {
|
||||||
static final List<GetPage> getPages = [
|
static final List<GetPage> getPages = [
|
||||||
@ -13,6 +14,8 @@ class Routes {
|
|||||||
// 视频详情
|
// 视频详情
|
||||||
GetPage(name: '/video', page: () => const VideoDetailPage()),
|
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"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
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:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -725,6 +733,46 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3"
|
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:
|
win32:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
19
pubspec.yaml
@ -63,9 +63,13 @@ dependencies:
|
|||||||
# webView
|
# webView
|
||||||
url_launcher: ^6.1.9
|
url_launcher: ^6.1.9
|
||||||
flutter_inappwebview: 5.4.4
|
flutter_inappwebview: 5.4.4
|
||||||
|
webview_cookie_manager: ^2.0.6
|
||||||
|
webview_flutter: ^4.2.0
|
||||||
|
|
||||||
extended_nested_scroll_view: ^6.0.0
|
extended_nested_scroll_view: ^6.0.0
|
||||||
font_awesome_flutter: ^10.4.0
|
font_awesome_flutter: ^10.4.0
|
||||||
|
# toast
|
||||||
|
flutter_smart_dialog: ^4.9.0+6
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
@ -77,6 +81,21 @@ dev_dependencies:
|
|||||||
# package. See that file for information about deactivating specific lint
|
# package. See that file for information about deactivating specific lint
|
||||||
# rules and activating additional ones.
|
# rules and activating additional ones.
|
||||||
flutter_lints: ^2.0.0
|
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
|
# For information on the generic Dart part of this file, see the
|
||||||
# following page: https://dart.dev/tools/pub/pubspec
|
# following page: https://dart.dev/tools/pub/pubspec
|
||||||
|