mod: homePage开发、mainPage优化

This commit is contained in:
guozhigq
2023-04-18 17:12:32 +08:00
parent efa257b175
commit fbfdc2138b
6 changed files with 330 additions and 32 deletions

View File

@ -0,0 +1,7 @@
import 'package:flutter/material.dart';
class StyleString {
static const double cardSpace = 8;
static BorderRadius mdRadius = BorderRadius.circular(6);
static const Radius imgRadius = Radius.circular(6);
}

View File

@ -0,0 +1,47 @@
import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:pilipala/http/api.dart';
import 'package:pilipala/http/init.dart';
class HomeController extends GetxController {
final ScrollController scrollController = ScrollController();
int count = 12;
int _currentPage = 1;
int crossAxisCount = 2;
RxList videoList = [].obs;
bool isLoadingMore = false;
@override
void onInit() {
super.onInit();
queryRcmdFeed('init');
}
// 获取推荐
Future queryRcmdFeed(type) async {
var res = await Request().get(
Api.recommendList,
data: {'feed_version': "V3", 'ps': count, 'fresh_idx': _currentPage},
);
var data = res.data['data']['item'];
if (type == 'init') {
videoList.value = data;
} else if (type == 'onRefresh') {
videoList.insertAll(0, data);
} else if (type == 'onLoad') {
videoList.addAll(data);
}
_currentPage += 1;
isLoadingMore = false;
}
// 下拉刷新
Future onRefresh() async {
queryRcmdFeed('onRefresh');
}
// 上拉加载
Future onLoad() async {
await Future.delayed(const Duration(milliseconds: 500));
queryRcmdFeed('onLoad');
}
}

View File

@ -1,18 +1,151 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import './controller.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/pages/home/widgets/app_bar.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
class _HomePageState extends State<HomePage>
with AutomaticKeepAliveClientMixin {
final HomeController _homeController = Get.put(HomeController());
List videoList = [];
@override
bool get wantKeepAlive => true;
@override
void initState() {
super.initState();
_homeController.videoList.listen((value) {
videoList = value;
setState(() {});
});
_homeController.scrollController.addListener(
() {
if (_homeController.scrollController.position.pixels >=
_homeController.scrollController.position.maxScrollExtent - 200) {
if (!_homeController.isLoadingMore) {
_homeController.isLoadingMore = true;
_homeController.onLoad();
}
}
},
);
}
@override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
title: const Text('推荐'),
// body: NestedScrollView(
// headerSliverBuilder: (context, innerBoxIsScrolled) => [
// const HomeAppBar()
// ],
body: RefreshIndicator(
displacement: kToolbarHeight + MediaQuery.of(context).padding.top,
onRefresh: () async {
return await _homeController.onRefresh();
},
child: CustomScrollView(
controller: _homeController.scrollController,
slivers: [
const HomeAppBar(),
// SliverPersistentHeader(
// delegate: MySliverPersistentHeaderDelegate(),
// pinned: true,
// ),
SliverPadding(
// 单列布局 EdgeInsets.zero
padding: _homeController.crossAxisCount == 1
? EdgeInsets.zero
: const EdgeInsets.fromLTRB(
StyleString.cardSpace, 0, StyleString.cardSpace, 8),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
// 行间距
mainAxisSpacing: StyleString.cardSpace,
// 列间距
crossAxisSpacing: StyleString.cardSpace,
// 列数
crossAxisCount: _homeController.crossAxisCount,
mainAxisExtent: MediaQuery.of(context).size.width /
_homeController.crossAxisCount *
(10 / 16) +
72),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
color: Theme.of(context).colorScheme.surfaceVariant,
child: Text(index.toString()),
);
},
childCount: videoList.isNotEmpty ? videoList.length : 10,
),
),
),
const LoadingMore()
],
),
),
// ),
);
}
}
class MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate {
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border(
bottom: BorderSide(
color: Theme.of(context).dividerColor.withOpacity(0.1),
),
)),
child: const Text(
'我是一个SliverPersistentHeader',
),
);
}
@override
double get maxExtent => 55.0;
@override
double get minExtent => 55.0;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) =>
true; // 如果内容需要更新设置为true
}
// loading more
class LoadingMore extends StatelessWidget {
const LoadingMore({super.key});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Container(
height: MediaQuery.of(context).padding.bottom + 80,
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
child: Center(
child: Text(
'加载中...',
style: TextStyle(
color: Theme.of(context).colorScheme.outline, fontSize: 13),
),
),
),
);
}

View File

@ -0,0 +1,58 @@
import 'dart:io';
import 'package:flutter/material.dart';
class HomeAppBar extends StatelessWidget {
const HomeAppBar({super.key});
@override
Widget build(BuildContext context) {
return SliverAppBar(
// forceElevated: true,
scrolledUnderElevation: 0,
toolbarHeight: Platform.isAndroid
? (MediaQuery.of(context).padding.top + 6)
: Platform.isIOS
? MediaQuery.of(context).padding.top - 2
: kToolbarHeight,
expandedHeight: kToolbarHeight + MediaQuery.of(context).padding.top,
automaticallyImplyLeading: false,
pinned: true,
floating: true,
primary: false,
flexibleSpace: LayoutBuilder(
builder: (context, constraints) {
return FlexibleSpaceBar(
background: Column(
children: [
AppBar(
centerTitle: false,
title: const Text(
'PiLiPaLa',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
letterSpacing: 1,
),
),
actions: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.notifications_none_rounded),
),
IconButton(
onPressed: () {},
icon: const Icon(Icons.search_rounded),
),
const SizedBox(width: 10)
],
elevation: 0,
scrolledUnderElevation: 0,
),
],
),
);
},
),
);
}
}

View File

@ -0,0 +1,30 @@
import 'package:get/get.dart';
import 'package:flutter/material.dart';
import 'package:pilipala/pages/home/view.dart';
import 'package:pilipala/pages/hot/view.dart';
import 'package:pilipala/pages/mine/view.dart';
class MainController extends GetxController {
List<Widget> pages = <Widget>[
const HomePage(),
const HotPage(),
const MinePage(),
];
List navigationBars = [
{
'icon': const Icon(Icons.home_outlined),
'selectedIcon': const Icon(Icons.home),
'label': "推荐",
},
{
'icon': const Icon(Icons.whatshot_outlined),
'selectedIcon': const Icon(Icons.whatshot_rounded),
'label': "热门",
},
{
'icon': const Icon(Icons.person_outline),
'selectedIcon': const Icon(Icons.person),
'label': "我的",
}
];
}

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:pilipala/pages/home/view.dart';
import 'package:pilipala/pages/hot/view.dart';
import 'package:pilipala/pages/mine/view.dart';
import 'package:get/get.dart';
import './controller.dart';
class MainApp extends StatefulWidget {
const MainApp({super.key});
@ -10,12 +9,35 @@ class MainApp extends StatefulWidget {
State<MainApp> createState() => _MainAppState();
}
class _MainAppState extends State<MainApp> {
class _MainAppState extends State<MainApp> with SingleTickerProviderStateMixin {
final MainController _mainController = Get.put(MainController());
late AnimationController? _animationController;
late Animation<double>? _fadeAnimation;
late Animation<double>? _slideAnimation;
int selectedIndex = 0;
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: const Duration(milliseconds: 800),
reverseDuration: const Duration(milliseconds: 0),
value: 1,
vsync: this,
);
_fadeAnimation =
Tween<double>(begin: 0.8, end: 1.0).animate(_animationController!);
_slideAnimation =
Tween(begin: 0.8, end: 1.0).animate(_animationController!);
}
void setIndex(int value) {
if (selectedIndex != value) {
selectedIndex = value;
_animationController!.reverse().then((_) {
selectedIndex = value;
_animationController!.forward();
});
setState(() {});
}
}
@ -23,33 +45,34 @@ class _MainAppState extends State<MainApp> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: selectedIndex,
children: const [
HomePage(),
HotPage(),
MinePage(),
],
body: FadeTransition(
opacity: _fadeAnimation!,
child: SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 0.5),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: _slideAnimation!,
curve: Curves.fastOutSlowIn,
reverseCurve: Curves.linear,
),
),
child: IndexedStack(
index: selectedIndex,
children: _mainController.pages,
),
),
),
bottomNavigationBar: NavigationBar(
elevation: 1,
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: "推荐",
),
NavigationDestination(
icon: Icon(Icons.whatshot_outlined),
selectedIcon: Icon(Icons.whatshot_rounded),
label: "热门",
),
NavigationDestination(
icon: Icon(Icons.person_outline),
label: "我的",
selectedIcon: Icon(Icons.person),
),
],
destinations: _mainController.navigationBars.map((e) {
return NavigationDestination(
icon: e['icon'],
selectedIcon: e['selectedIcon'],
label: e['label'],
);
}).toList(),
selectedIndex: selectedIndex,
onDestinationSelected: (value) => setIndex(value),
),