仓库地址:
https://gitee.com/zhangNull/flutter_valorent
https://github.com/nullErrorzy/flutter_valorent
效果图:
实现组件:
一、拆分组件
1:实现组件
Column(
children: [
Align(
alignment: Alignment.centerRight,
child: GestureDetector(
onTap: () => Get.to(() => const MorePage(),
transition: Transition.rightToLeft),
child: SvgPicture.asset('assets/images/menu.svg'))),
const SizedBox(width: double.infinity, height: 10),
SvgPicture.asset('assets/images/Vector 3.svg'),
const SizedBox(height: 10),
Text('Choose your', style: titleStyle),
Text('awesome agent', style: titleStyle),
)
],
),
2:实现组件
Column(
children: [
TabBar(
dividerColor: Colors.transparent,
labelStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'MiSans'),
indicatorSize: TabBarIndicatorSize.tab,
indicatorPadding: const EdgeInsets.symmetric(horizontal: 50),
controller: tabController,
indicatorColor: const Color.fromARGB(255, 228, 86, 102),
overlayColor:
const MaterialStatePropertyAll(Colors.transparent),
unselectedLabelColor: const Color.fromARGB(255, 181, 184, 190),
labelColor: const Color.fromARGB(255, 228, 86, 102),
indicatorWeight: 2,
tabs: const [
Tab(child: Text("Popular")),
Tab(child: Text("New One")),
Tab(child: Text("More")),
]),
const SizedBox(height: 5),
Expanded(
child: TabBarView(controller: tabController, children: const [
MyTabViewWidget(),
MyTabViewWidget(),
MyTabViewWidget(),
]))
],
),
MyTabViewWidget
class MyTabViewWidget extends StatelessWidget {
const MyTabViewWidget({super.key});
@override
Widget build(BuildContext context) {
return ListView(children: [
Row(
children: [
Expanded(
flex: 7,
child: Stack(children: [
SvgPicture.asset('assets/images/Rectangle 1.svg', height: 230),
Positioned(
right: 0,
child: SvgPicture.asset('assets/images/image 2.svg',
fit: BoxFit.cover)),
const Positioned(
bottom: 30,
left: 25,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('O M E N',
style: TextStyle(color: Colors.white, fontSize: 20)),
Text('Controllar',
style: TextStyle(color: Colors.white, fontSize: 18))
]),
)
]),
),
const SizedBox(width: 5),
Expanded(
flex: 6,
child: Stack(children: [
SvgPicture.asset('assets/images/Rectangle 2.svg', height: 230),
Positioned(
left: 10,
child: SvgPicture.asset('assets/images/image 3.svg',
fit: BoxFit.cover)),
const Positioned(
bottom: 30,
left: 30,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('P H O N I X',
style: TextStyle(color: Colors.white, fontSize: 20)),
Text('Duelist',
style: TextStyle(color: Colors.white, fontSize: 18))
]),
)
]),
),
],
),
const SizedBox(height: 10),
Row(children: [
Expanded(
flex: 5,
child: Stack(children: [
SvgPicture.asset('assets/images/Rectangle 3.svg', height: 230),
Positioned(
right: 0,
child: SvgPicture.asset('assets/images/image 4.svg',
fit: BoxFit.cover)),
const Positioned(
bottom: 30,
left: 15,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('R A Z E',
style: TextStyle(color: Colors.white, fontSize: 20)),
Text('Duelist',
style: TextStyle(color: Colors.white, fontSize: 18))
]),
)
]),
),
Expanded(
flex: 4,
child: Stack(children: [
SvgPicture.asset('assets/images/Rectangle 4.svg', height: 230),
Positioned(
right: 0,
child: SvgPicture.asset('assets/images/image 5.svg',
fit: BoxFit.cover)),
const Positioned(
bottom: 30,
left: 40,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('V I P E R',
style: TextStyle(color: Colors.white, fontSize: 20)),
Text('Controllar',
style: TextStyle(color: Colors.white, fontSize: 18))
]),
)
]),
),
])
]);
}
}
3.实现组件
CustomScrollView(slivers: [
SliverAppBar(
systemOverlayStyle: SystemUiOverlayStyle.light,
expandedHeight: 240,
flexibleSpace: Stack(
children: [
SvgPicture.asset(
"assets/images/Frame 2.svg",
fit: BoxFit.cover,
width: double.infinity,
height: 350,
),
Align(
alignment: Alignment.topRight,
child: SvgPicture.asset(
"assets/images/image 6.svg",
height: 300,
width: double.infinity,
fit: BoxFit.cover,
),
),
Positioned(
bottom: 50,
left: 30,
child: Column(
children: [
const Text('O M E N',
style: TextStyle(color: Colors.white, fontSize: 28)),
Container(
padding:
const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: const Color.fromARGB(255, 31, 32, 82),
borderRadius: BorderRadius.circular(6),
),
child: const Text('Controllar',
style: TextStyle(
color: Colors.white, fontFamily: 'MiSans')),
)
],
),
)
],
),
),
)]
4.实现组件
SliverList.list(children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
child: Column(
children: [
Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: const Color.fromARGB(255, 182, 182, 182),
borderRadius: BorderRadius.circular(10)),
),
const SizedBox(width: 5),
const Text('BIOGRAPHY',
style: TextStyle(color: Colors.white, fontSize: 24)),
],
),
const SizedBox(height: 5),
const Text(
'A phantom of a memory. Omen hunts in the shadows. He renders enemies blind, teleports across the field ',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontFamily: 'MiSans',
fontWeight: FontWeight.bold)),
const SizedBox(height: 50),
Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: const Color.fromARGB(255, 182, 182, 182),
borderRadius: BorderRadius.circular(10)),
),
const SizedBox(width: 5),
const Text('SPECIAL ABILITIES',
style: TextStyle(color: Colors.white, fontSize: 24)),
],
),
const SizedBox(height: 20),
buildItem(
url: 'Vector 2.svg',
title: 'SHROUDED',
body: 'Shadow walk & see its range indicator '),
const SizedBox(height: 20),
buildItem(
url: 'Group 4.svg',
title: 'PARANOIA',
body: 'Shadow projectile & blinden enemy'),
const SizedBox(height: 20),
buildItem(
url: 'Group 8.svg',
title: 'PARANOIA',
body: 'Black orb and see its range indicator'),
const SizedBox(height: 20),
buildItem(
url: 'Group 9.svg',
title: 'PARANOIA',
body: 'Tectical map to teleporting all areas'),
const SizedBox(height: 20),
buildItem(
url: 'Vector 2.svg',
title: 'SHROUDED',
body: 'Shadow walk & see its range indicator '),
const SizedBox(height: 20),
buildItem(
url: 'Group 4.svg',
title: 'PARANOIA',
body: 'Shadow projectile & blinden enemy'),
const SizedBox(height: 20),
buildItem(
url: 'Group 8.svg',
title: 'PARANOIA',
body: 'Black orb and see its range indicator'),
const SizedBox(height: 20),
buildItem(
url: 'Group 9.svg',
title: 'PARANOIA',
body: 'Tectical map to teleporting all areas'),
const SizedBox(height: 20),
],
),
)
])
buildItem
Widget buildItem(
{required String url, required String title, required String body}) {
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color.fromARGB(255, 19, 33, 46),
borderRadius: BorderRadius.circular(8)),
child: Row(children: [
SvgPicture.asset('assets/images/$url'),
const SizedBox(width: 10),
Expanded(
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(title, style: const TextStyle(color: Colors.white)),
Text(body,
style: const TextStyle(color: Colors.white, fontFamily: 'MiSans'))
]),
)
]),
);
}
最后上全部代码
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:flutter_valorent/more_page.dart';
import 'package:get/get.dart';
class ValorentPage extends StatefulWidget {
const ValorentPage({super.key});
@override
State<ValorentPage> createState() => _ValorentPageState();
}
class _ValorentPageState extends State<ValorentPage>
with TickerProviderStateMixin {
late TabController tabController;
@override
void initState() {
tabController = TabController(length: 3, vsync: this);
super.initState();
}
@override
Widget build(BuildContext context) {
TextStyle titleStyle = const TextStyle(
color: Colors.white, fontSize: 40, fontWeight: FontWeight.bold);
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: GestureDetector(
onTap: () => Get.to(() => const MorePage(),
transition: Transition.rightToLeft),
child: SvgPicture.asset('assets/images/menu.svg'))),
const SizedBox(width: double.infinity, height: 10),
SvgPicture.asset('assets/images/Vector 3.svg'),
const SizedBox(height: 10),
Text('Choose your', style: titleStyle),
Text('awesome agent', style: titleStyle),
TabBar(
dividerColor: Colors.transparent,
labelStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
fontFamily: 'MiSans'),
indicatorSize: TabBarIndicatorSize.tab,
indicatorPadding: const EdgeInsets.symmetric(horizontal: 50),
controller: tabController,
indicatorColor: const Color.fromARGB(255, 228, 86, 102),
overlayColor:
const MaterialStatePropertyAll(Colors.transparent),
unselectedLabelColor: const Color.fromARGB(255, 181, 184, 190),
labelColor: const Color.fromARGB(255, 228, 86, 102),
indicatorWeight: 2,
tabs: const [
Tab(child: Text("Popular")),
Tab(child: Text("New One")),
Tab(child: Text("More")),
]),
const SizedBox(height: 5),
Expanded(
child: TabBarView(controller: tabController, children: const [
MyTabViewWidget(),
MyTabViewWidget(),
MyTabViewWidget(),
]))
],
),
),
));
}
}
class MyTabViewWidget extends StatelessWidget {
const MyTabViewWidget({super.key});
@override
Widget build(BuildContext context) {
return ListView(children: [
Row(
children: [
Expanded(
flex: 7,
child: Stack(children: [
SvgPicture.asset('assets/images/Rectangle 1.svg', height: 230),
Positioned(
right: 0,
child: SvgPicture.asset('assets/images/image 2.svg',
fit: BoxFit.cover)),
const Positioned(
bottom: 30,
left: 25,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('O M E N',
style: TextStyle(color: Colors.white, fontSize: 20)),
Text('Controllar',
style: TextStyle(color: Colors.white, fontSize: 18))
]),
)
]),
),
const SizedBox(width: 5),
Expanded(
flex: 6,
child: Stack(children: [
SvgPicture.asset('assets/images/Rectangle 2.svg', height: 230),
Positioned(
left: 10,
child: SvgPicture.asset('assets/images/image 3.svg',
fit: BoxFit.cover)),
const Positioned(
bottom: 30,
left: 30,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('P H O N I X',
style: TextStyle(color: Colors.white, fontSize: 20)),
Text('Duelist',
style: TextStyle(color: Colors.white, fontSize: 18))
]),
)
]),
),
],
),
const SizedBox(height: 10),
Row(children: [
Expanded(
flex: 5,
child: Stack(children: [
SvgPicture.asset('assets/images/Rectangle 3.svg', height: 230),
Positioned(
right: 0,
child: SvgPicture.asset('assets/images/image 4.svg',
fit: BoxFit.cover)),
const Positioned(
bottom: 30,
left: 15,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('R A Z E',
style: TextStyle(color: Colors.white, fontSize: 20)),
Text('Duelist',
style: TextStyle(color: Colors.white, fontSize: 18))
]),
)
]),
),
Expanded(
flex: 4,
child: Stack(children: [
SvgPicture.asset('assets/images/Rectangle 4.svg', height: 230),
Positioned(
right: 0,
child: SvgPicture.asset('assets/images/image 5.svg',
fit: BoxFit.cover)),
const Positioned(
bottom: 30,
left: 40,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('V I P E R',
style: TextStyle(color: Colors.white, fontSize: 20)),
Text('Controllar',
style: TextStyle(color: Colors.white, fontSize: 18))
]),
)
]),
),
])
]);
}
}
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
class MorePage extends StatefulWidget {
const MorePage({super.key});
@override
State<MorePage> createState() => _MorePageState();
}
class _MorePageState extends State<MorePage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(slivers: [
SliverAppBar(
systemOverlayStyle: SystemUiOverlayStyle.light,
expandedHeight: 240,
flexibleSpace: Stack(
children: [
SvgPicture.asset(
"assets/images/Frame 2.svg",
fit: BoxFit.cover,
width: double.infinity,
height: 350,
),
Align(
alignment: Alignment.topRight,
child: SvgPicture.asset(
"assets/images/image 6.svg",
height: 300,
width: double.infinity,
fit: BoxFit.cover,
),
),
Positioned(
bottom: 50,
left: 30,
child: Column(
children: [
const Text('O M E N',
style: TextStyle(color: Colors.white, fontSize: 28)),
Container(
padding:
const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: const Color.fromARGB(255, 31, 32, 82),
borderRadius: BorderRadius.circular(6),
),
child: const Text('Controllar',
style: TextStyle(
color: Colors.white, fontFamily: 'MiSans')),
)
],
),
)
],
),
),
SliverList.list(children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
child: Column(
children: [
Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: const Color.fromARGB(255, 182, 182, 182),
borderRadius: BorderRadius.circular(10)),
),
const SizedBox(width: 5),
const Text('BIOGRAPHY',
style: TextStyle(color: Colors.white, fontSize: 24)),
],
),
const SizedBox(height: 5),
const Text(
'A phantom of a memory. Omen hunts in the shadows. He renders enemies blind, teleports across the field ',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontFamily: 'MiSans',
fontWeight: FontWeight.bold)),
const SizedBox(height: 50),
Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: const Color.fromARGB(255, 182, 182, 182),
borderRadius: BorderRadius.circular(10)),
),
const SizedBox(width: 5),
const Text('SPECIAL ABILITIES',
style: TextStyle(color: Colors.white, fontSize: 24)),
],
),
const SizedBox(height: 20),
buildItem(
url: 'Vector 2.svg',
title: 'SHROUDED',
body: 'Shadow walk & see its range indicator '),
const SizedBox(height: 20),
buildItem(
url: 'Group 4.svg',
title: 'PARANOIA',
body: 'Shadow projectile & blinden enemy'),
const SizedBox(height: 20),
buildItem(
url: 'Group 8.svg',
title: 'PARANOIA',
body: 'Black orb and see its range indicator'),
const SizedBox(height: 20),
buildItem(
url: 'Group 9.svg',
title: 'PARANOIA',
body: 'Tectical map to teleporting all areas'),
const SizedBox(height: 20),
buildItem(
url: 'Vector 2.svg',
title: 'SHROUDED',
body: 'Shadow walk & see its range indicator '),
const SizedBox(height: 20),
buildItem(
url: 'Group 4.svg',
title: 'PARANOIA',
body: 'Shadow projectile & blinden enemy'),
const SizedBox(height: 20),
buildItem(
url: 'Group 8.svg',
title: 'PARANOIA',
body: 'Black orb and see its range indicator'),
const SizedBox(height: 20),
buildItem(
url: 'Group 9.svg',
title: 'PARANOIA',
body: 'Tectical map to teleporting all areas'),
const SizedBox(height: 20),
],
),
)
])
]));
}
}
Widget buildItem(
{required String url, required String title, required String body}) {
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color.fromARGB(255, 19, 33, 46),
borderRadius: BorderRadius.circular(8)),
child: Row(children: [
SvgPicture.asset('assets/images/$url'),
const SizedBox(width: 10),
Expanded(
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(title, style: const TextStyle(color: Colors.white)),
Text(body,
style: const TextStyle(color: Colors.white, fontFamily: 'MiSans'))
]),
)
]),
);
}