improve: 首页样式改动
- 渐变的背景 - tab 调整
This commit is contained in:
@ -30,7 +30,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
stream = _homeController.searchBarStream.stream;
|
stream = _homeController.searchBarStream.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
showUserBottonSheet() {
|
showUserBottomSheet() {
|
||||||
feedBack();
|
feedBack();
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
@ -49,42 +49,40 @@ class _HomePageState extends State<HomePage>
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
appBar: AppBar(toolbarHeight: 0, elevation: 0),
|
body: Stack(
|
||||||
body: Column(
|
children: [
|
||||||
|
// gradient background
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.topLeft,
|
||||||
|
child: Opacity(
|
||||||
|
opacity: 0.6,
|
||||||
|
child: Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: [
|
||||||
|
Theme.of(context).colorScheme.primary.withOpacity(0.9),
|
||||||
|
Theme.of(context).colorScheme.primary.withOpacity(0.5),
|
||||||
|
Theme.of(context).colorScheme.surface
|
||||||
|
],
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
stops: const [0, 0.0034, 0.34]),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
children: [
|
children: [
|
||||||
CustomAppBar(
|
CustomAppBar(
|
||||||
stream: _homeController.hideSearchBar
|
stream: _homeController.hideSearchBar
|
||||||
? stream
|
? stream
|
||||||
: StreamController<bool>.broadcast().stream,
|
: StreamController<bool>.broadcast().stream,
|
||||||
ctr: _homeController,
|
ctr: _homeController,
|
||||||
callback: showUserBottonSheet,
|
callback: showUserBottomSheet,
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 42,
|
|
||||||
child: Align(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
child: TabBar(
|
|
||||||
controller: _homeController.tabController,
|
|
||||||
tabs: [
|
|
||||||
for (var i in _homeController.tabs) Tab(text: i['label'])
|
|
||||||
],
|
|
||||||
isScrollable: true,
|
|
||||||
dividerColor: Colors.transparent,
|
|
||||||
enableFeedback: true,
|
|
||||||
splashBorderRadius: BorderRadius.circular(10),
|
|
||||||
tabAlignment: TabAlignment.center,
|
|
||||||
onTap: (value) {
|
|
||||||
feedBack();
|
|
||||||
if (_homeController.initialIndex == value) {
|
|
||||||
_homeController.tabsCtrList[value]().animateToTop();
|
|
||||||
}
|
|
||||||
_homeController.initialIndex = value;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
const CustomTabs(),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
controller: _homeController.tabController,
|
controller: _homeController.tabController,
|
||||||
@ -93,6 +91,8 @@ class _HomePageState extends State<HomePage>
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,24 +128,33 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
height: snapshot.data
|
height: snapshot.data
|
||||||
? MediaQuery.of(context).padding.top + 52
|
? MediaQuery.of(context).padding.top + 52
|
||||||
: MediaQuery.of(context).padding.top - 10,
|
: MediaQuery.of(context).padding.top + 5,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
left: 20,
|
left: 8,
|
||||||
right: 20,
|
right: 8,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
top: MediaQuery.of(context).padding.top + 4,
|
top: MediaQuery.of(context).padding.top + 4,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
const Expanded(child: SearchPage()),
|
Expanded(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/images/logo/logo_android_2.png',
|
||||||
|
height: 48,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
const SearchPage(),
|
||||||
if (ctr!.userLogin.value) ...[
|
if (ctr!.userLogin.value) ...[
|
||||||
const SizedBox(width: 6),
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Get.toNamed('/whisper'),
|
onPressed: () => Get.toNamed('/whisper'),
|
||||||
icon: const Icon(Icons.notifications_none))
|
icon: const Icon(Icons.notifications_none))
|
||||||
],
|
],
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 8),
|
||||||
Obx(
|
Obx(
|
||||||
() => ctr!.userLogin.value
|
() => ctr!.userLogin.value
|
||||||
? Stack(
|
? Stack(
|
||||||
@ -191,7 +200,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
),
|
),
|
||||||
onPressed: () => callback!(),
|
onPressed: () => callback!(),
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.person_rounded,
|
Icons.account_circle,
|
||||||
size: 22,
|
size: 22,
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
@ -207,3 +216,108 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CustomTabs extends StatefulWidget {
|
||||||
|
const CustomTabs({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CustomTabs> createState() => _CustomTabsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomTabsState extends State<CustomTabs> {
|
||||||
|
final HomeController _homeController = Get.put(HomeController());
|
||||||
|
int currentTabIndex = 1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_homeController.tabController.addListener(listen);
|
||||||
|
currentTabIndex = _homeController.tabController.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void listen() {
|
||||||
|
setState(() {
|
||||||
|
currentTabIndex = _homeController.tabController.index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void onTap(int index) {
|
||||||
|
feedBack();
|
||||||
|
if (_homeController.initialIndex == index) {
|
||||||
|
_homeController.tabsCtrList[index]().animateToTop();
|
||||||
|
}
|
||||||
|
_homeController.initialIndex = index;
|
||||||
|
setState(() {
|
||||||
|
_homeController.tabController.index = index;
|
||||||
|
currentTabIndex = index;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
_homeController.tabController.removeListener(listen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 56,
|
||||||
|
child: ListView.separated(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: _homeController.tabs.length,
|
||||||
|
separatorBuilder: (BuildContext context, int index) {
|
||||||
|
return const SizedBox(width: 8.0);
|
||||||
|
},
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
bool selected = index == currentTabIndex;
|
||||||
|
String label = _homeController.tabs[index]['label'];
|
||||||
|
// add margins to first and last tab;
|
||||||
|
return CustomChip(
|
||||||
|
onTap: () {
|
||||||
|
onTap(index);
|
||||||
|
},
|
||||||
|
label: label,
|
||||||
|
selected: selected);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomChip extends StatelessWidget {
|
||||||
|
final Function onTap;
|
||||||
|
final String label;
|
||||||
|
final bool selected;
|
||||||
|
const CustomChip(
|
||||||
|
{super.key,
|
||||||
|
required this.onTap,
|
||||||
|
required this.label,
|
||||||
|
required this.selected});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
TextStyle? chipTextStyle =
|
||||||
|
selected ? const TextStyle(fontWeight: FontWeight.bold) : null;
|
||||||
|
|
||||||
|
return InputChip(
|
||||||
|
side: const BorderSide(color: Colors.transparent),
|
||||||
|
color: MaterialStateProperty.resolveWith<Color>((Set<MaterialState> states) {
|
||||||
|
if (states.contains(MaterialState.selected)) {
|
||||||
|
return Theme.of(context).colorScheme.tertiaryContainer; // 当按钮被按下时的颜色
|
||||||
|
}
|
||||||
|
return Theme.of(context).colorScheme.surfaceVariant; // 默认颜色
|
||||||
|
}),
|
||||||
|
label: Text(
|
||||||
|
label,
|
||||||
|
style: chipTextStyle,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
onTap();
|
||||||
|
},
|
||||||
|
selected: selected,
|
||||||
|
showCheckmark: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -70,8 +70,7 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
return Scaffold(
|
return RefreshIndicator(
|
||||||
body: RefreshIndicator(
|
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
return await _hotController.onRefresh();
|
return await _hotController.onRefresh();
|
||||||
},
|
},
|
||||||
@ -132,7 +131,6 @@ class _HotPageState extends State<HotPage> with AutomaticKeepAliveClientMixin {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,11 +19,11 @@ class MainController extends GetxController {
|
|||||||
RxList navigationBars = [
|
RxList navigationBars = [
|
||||||
{
|
{
|
||||||
'icon': const Icon(
|
'icon': const Icon(
|
||||||
Icons.favorite_outline,
|
Icons.home_outlined,
|
||||||
size: 21,
|
size: 21,
|
||||||
),
|
),
|
||||||
'selectIcon': const Icon(
|
'selectIcon': const Icon(
|
||||||
Icons.favorite,
|
Icons.home,
|
||||||
size: 21,
|
size: 21,
|
||||||
),
|
),
|
||||||
'label': "首页",
|
'label': "首页",
|
||||||
@ -41,11 +41,11 @@ class MainController extends GetxController {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
'icon': const Icon(
|
'icon': const Icon(
|
||||||
Icons.folder_outlined,
|
Icons.video_collection_outlined,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
'selectIcon': const Icon(
|
'selectIcon': const Icon(
|
||||||
Icons.folder,
|
Icons.video_collection,
|
||||||
size: 21,
|
size: 21,
|
||||||
),
|
),
|
||||||
'label': "媒体库",
|
'label': "媒体库",
|
||||||
|
|||||||
@ -48,53 +48,54 @@ class _SearchPageState extends State<SearchPage> with RouteAware {
|
|||||||
onClosed: (_) => _searchController.onClear(),
|
onClosed: (_) => _searchController.onClear(),
|
||||||
openColor: Theme.of(context).colorScheme.background,
|
openColor: Theme.of(context).colorScheme.background,
|
||||||
middleColor: Theme.of(context).colorScheme.background,
|
middleColor: Theme.of(context).colorScheme.background,
|
||||||
closedColor: Theme.of(context).colorScheme.background,
|
closedColor: Colors.transparent,
|
||||||
closedShape: const RoundedRectangleBorder(
|
closedShape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(30.0))),
|
borderRadius: BorderRadius.all(Radius.circular(30.0))),
|
||||||
openShape: const RoundedRectangleBorder(
|
openShape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(Radius.circular(30.0))),
|
borderRadius: BorderRadius.all(Radius.circular(30.0))),
|
||||||
closedBuilder: (BuildContext context, VoidCallback openContainer) {
|
closedBuilder: (BuildContext context, VoidCallback openContainer) {
|
||||||
return Container(
|
return IconButton(onPressed: openContainer, icon: Icon(Icons.search));
|
||||||
width: 250,
|
// return Container(
|
||||||
height: 44,
|
// width: 250,
|
||||||
clipBehavior: Clip.hardEdge,
|
// height: 44,
|
||||||
decoration: const BoxDecoration(
|
// clipBehavior: Clip.hardEdge,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
// decoration: const BoxDecoration(
|
||||||
),
|
// borderRadius: BorderRadius.all(Radius.circular(25)),
|
||||||
child: Material(
|
// ),
|
||||||
color:
|
// child: Material(
|
||||||
Theme.of(context).colorScheme.secondaryContainer.withAlpha(115),
|
// color:
|
||||||
child: InkWell(
|
// Theme.of(context).colorScheme.secondaryContainer.withAlpha(115),
|
||||||
splashColor: Theme.of(context)
|
// child: InkWell(
|
||||||
.colorScheme
|
// splashColor: Theme.of(context)
|
||||||
.primaryContainer
|
// .colorScheme
|
||||||
.withOpacity(0.3),
|
// .primaryContainer
|
||||||
onTap: openContainer,
|
// .withOpacity(0.3),
|
||||||
child: Row(
|
// onTap: openContainer,
|
||||||
children: [
|
// child: Row(
|
||||||
const SizedBox(width: 14),
|
// children: [
|
||||||
Icon(
|
// const SizedBox(width: 14),
|
||||||
Icons.search_outlined,
|
// Icon(
|
||||||
color: Theme.of(context).colorScheme.onSecondaryContainer,
|
// Icons.search_outlined,
|
||||||
),
|
// color: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||||
const SizedBox(width: 10),
|
// ),
|
||||||
Expanded(
|
// const SizedBox(width: 10),
|
||||||
child: Obx(
|
// Expanded(
|
||||||
() => Text(
|
// child: Obx(
|
||||||
_searchController.defaultSearch.value,
|
// () => Text(
|
||||||
maxLines: 1,
|
// _searchController.defaultSearch.value,
|
||||||
overflow: TextOverflow.ellipsis,
|
// maxLines: 1,
|
||||||
style: TextStyle(
|
// overflow: TextOverflow.ellipsis,
|
||||||
color: Theme.of(context).colorScheme.outline,
|
// style: TextStyle(
|
||||||
),
|
// color: Theme.of(context).colorScheme.outline,
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
],
|
// ),
|
||||||
),
|
// ],
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
);
|
// ),
|
||||||
|
// );
|
||||||
},
|
},
|
||||||
openBuilder: (BuildContext context, VoidCallback _) {
|
openBuilder: (BuildContext context, VoidCallback _) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
|||||||
Reference in New Issue
Block a user