引入:
flutter_swiper_null_safety: ^1.0.0
flutter_staggered_grid_view: ^0.6.1
4张样式图:
代码:
- homePage.dart
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'dart:ui';
import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart';
import 'StickyTabBarDelegate.dart';
class Main extends StatefulWidget {
const Main({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<Main> createState() => _MainState();
}
class _MainState extends State<Main> {
var _selectedIndex = 0;
final _random = Random();
final ScrollController _controller = ScrollController();
final data = List.generate(15, (i) => Color(0xffaa1dff - 125 * i));
var _onTypeClick = 0;
final TextEditingController _editController = TextEditingController();
final FocusNode _commentFocus = FocusNode();
@override
Widget build(BuildContext context) {
_controller.addListener(() {
if (_controller.position.pixels == _controller.position.maxScrollExtent) {
_loadMoreData();
}
});
_commentFocus.addListener(() {
setState(() {});
});
_editController.addListener(() {
setState(() {});
});
_waterFallList.clear();
for (int i = 0; i < 24; i++) {
_waterFallList.add(_random.nextDouble() * 50 + 50);
}
return Scaffold(
body: _createBody(_selectedIndex),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedIndex,
type: BottomNavigationBarType.fixed,
onTap: (int index) {
_selectedIndex = index;
setState(() {});
},
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.favorite),
label: 'favorite',
),
BottomNavigationBarItem(
icon: Icon(Icons.save),
label: 'save',
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: 'settings',
),
],
),
);
}
List<Image> imageList = [
Image.asset(
'images/1.jpg',
fit: BoxFit.fill,
),
Image.asset(
'images/2.jpg',
fit: BoxFit.fill,
),
Image.network(
"https://www.baidu.com/img/PC_9d6532110a742ba494be893d19bc80f8.png",
fit: BoxFit.cover,
),
];
final List _waterFallList = [];
Widget _createBody(int index) {
switch (index) {
case 0:
return CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate((content, index) {
return Center(
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0, MediaQueryData.fromWindow(window).padding.top, 0, 0),
child: Column(
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
height: 40,
width: MediaQuery.of(context).size.width / 10,
color: Colors.blue,
child:
const Icon(Icons.accessible_forward_rounded)),
Container(
height: 40,
width: MediaQuery.of(context).size.width / 10,
color: Colors.blue,
child: const Icon(Icons.accessible)),
Container(
alignment: Alignment.center,
height: 40,
color: Colors.blue,
padding: const EdgeInsets.all(6),
width: MediaQuery.of(context).size.width / 10 * 7,
child: TextField(
controller: _editController,
focusNode: _commentFocus,
decoration: InputDecoration(
helperMaxLines: 1,
contentPadding: const EdgeInsetsDirectional.fromSTEB(
10, 0, 10, 0),
prefixIcon: _commentFocus.hasFocus
? const Icon(Icons.search, color: Colors.black)
: null,
suffixIcon: _editController.text.isNotEmpty
? const Icon(Icons.clear)
: null,
iconColor: Colors.black,
filled: true,
enabledBorder: const OutlineInputBorder(
borderSide:
BorderSide(color: Color(0x00FF0000)),
borderRadius:
BorderRadius.all(Radius.circular(20))),
hintText: ' 内容',
focusedBorder: const OutlineInputBorder(
borderSide: BorderSide(
color: Color(0x00000000)),
borderRadius: BorderRadius.all(
Radius.circular(20))),
),
),
),
Container(
height: 40,
width: MediaQuery.of(context).size.width / 10,
color: Colors.blue,
child: const Icon(Icons.accessibility_rounded)),
],
),
SizedBox(
width: MediaQuery.of(context).size.width,
height: 190.0,
child: Swiper(
itemBuilder: (BuildContext context, int index) {
return imageList[index];
},
loop: true,
index: 2,
autoplay: true,
duration: 600,
viewportFraction: 0.8,
scale: 0.2,
layout: SwiperLayout.TINDER,
itemCount: imageList.length,
pagination: const SwiperPagination(),
control: const SwiperControl(),
)),
GridView(
shrinkWrap: true,
controller: _controller,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 5,
crossAxisSpacing: 30,
mainAxisSpacing: 10),
children: [
_createGridViewItem(Colors.primaries[1]),
_createGridViewItem(Colors.primaries[2]),
_createGridViewItem(Colors.primaries[3]),
_createGridViewItem(Colors.primaries[4]),
_createGridViewItem(Colors.primaries[5]),
_createGridViewItem(Colors.primaries[6]),
_createGridViewItem(Colors.primaries[7]),
_createGridViewItem(Colors.primaries[8]),
_createGridViewItem(Colors.primaries[9]),
_createGridViewItem(Colors.primaries[10]),
],
),
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
alignment: Alignment.center,
//设置控件内容的位置
height: 60,
width: MediaQuery.of(context).size.width / 4,
margin: const EdgeInsets.all(5),
child: const Expanded(
child: Text(
'文本框选项 ',
style: TextStyle(fontSize: 18),
)),
),
_createCardViewItem(Colors.primaries[1]),
_createCardViewItem(Colors.primaries[1]),
_createCardViewItem(Colors.primaries[1]),
],
),
],
),
),
MasonryGridView.count(
scrollDirection: Axis.vertical,
// 展示几列
crossAxisCount: 2,
padding: const EdgeInsets.all(8),
// 元素总个数
itemCount: _waterFallList.length,
// 单个子元素
itemBuilder: (context, index) {
return _waterCard(_waterFallList[index]);
},
// 纵向元素间距
mainAxisSpacing: 10,
// 横向元素间距
crossAxisSpacing: 10,
//本身不滚动,让外面的scrollview来滚动
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, //收缩,让元素宽度自适应
)
],
),
));
}, childCount: 1),
)
],
);
case 1:
return Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0, MediaQueryData.fromWindow(window).padding.top, 0, 0),
child: Row(
children: [
SizedBox(
width: MediaQuery.of(context).size.width / 4,
child: ListView.builder(
itemCount: 12,
itemExtent: 26,
itemBuilder: (BuildContext c, int index) {
return _buildFavouriteTypeItem(index);
},
physics: const NeverScrollableScrollPhysics(),
),
),
SizedBox(
width: MediaQuery.of(context).size.width / 4 * 2.7,
child: Center(
child: GridView(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 8,
mainAxisSpacing: 8),
children: data
.map((color) => _buildFavouriteItem(color))
.toList(),
),
)),
],
),
);
case 2:
return _createSTABBAR();
default:
return Padding(
padding: EdgeInsetsDirectional.fromSTEB(
0, MediaQueryData.fromWindow(window).padding.top, 0, 0),
child: Column(
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.3,
color: Colors.grey,
child: const Center(
child: CircleAvatar(
radius: 50,
backgroundColor: Colors.white, //未设置背景色,加载图片时会显示红色
backgroundImage: NetworkImage(
"https://desk-fd.zol-img.com.cn/t_s960x600c5/g6/M00/03/0E/ChMkKWDZLXSICljFAC1U9uUHfekAARQfgG_oL0ALVUO515.jpg"),
)),
),
const Text("标题1"),
Card(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
height: 52,
child: Row(children: [
_createCardViewItem(Colors.deepOrange),
_createCardViewItem(Colors.deepPurple),
_createCardViewItem(Colors.deepOrange),
_createCardViewItem(Colors.deepPurple)
])),
),
const Text('我的'),
Card(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
height: 52,
child: const ListTile(
leading: Icon(Icons.access_alarm_rounded),
title: Text('横条设置item1'),
),
)),
Card(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
height: 52,
child: const ListTile(
leading: Icon(Icons.safety_divider_outlined),
title: Text('横条设置item2'),
),
)),
Card(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
height: 52,
child: const ListTile(
leading: Icon(Icons.settings),
title: Text('横条设置item3'),
),
)),
Card(
child: SizedBox(
width: MediaQuery.of(context).size.width * 0.9,
height: 52,
child: const ListTile(
leading: Icon(Icons.sanitizer_sharp),
title: Text('横条设置item6'),
),
)),
],
));
}
}
Widget _createGridViewItem(Color color) {
return Container(
alignment: Alignment.center,
//设置控件内容的位置
height: 20,
color: color,
margin: const EdgeInsets.all(5),
child: const Text('小标签'),
);
}
Widget _createCardViewItem(Color color) {
return Container(
alignment: Alignment.center,
//设置控件内容的位置
height: 40,
width: MediaQuery.of(context).size.width / 5,
color: color,
margin: const EdgeInsets.all(5),
child: const Text('标签内容'),
);
}
Widget _waterCard(double item) {
return Container(
height: item,
decoration: BoxDecoration(
border: Border.all(color: Colors.orange, width: 1),
borderRadius: BorderRadius.circular(10)),
child: Center(
child: Text("卡片Item高度:${item.toStringAsFixed(0)}"),
),
);
}
Widget _createSTABBAR() {
var tabTitle = [
'tab1',
'tab2',
'tab3',
];
const url =
'http://www.pptbz.com/pptpic/UploadFiles_6909/201203/2012031220134655.jpg';
return DefaultTabController(
length: tabTitle.length,
child: Scaffold(
body: NestedScrollView(
headerSliverBuilder: (context, innerBoxIsScrolled) {
return [
SliverAppBar(
expandedHeight: 180.0,
floating: true,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
centerTitle: true,
title: const Text(
"title",
),
background: Image.network(
url,
fit: BoxFit.cover,
)),
),
SliverPersistentHeader(
delegate: SliverTabBarDelegate(
TabBar(
tabs: tabTitle.map((f) => Tab(text: f)).toList(),
indicatorColor: Colors.red,
unselectedLabelColor: Colors.black,
labelColor: Colors.red,
),
color: Colors.white,
),
pinned: true,
),
];
},
body: TabBarView(
children: tabTitle
.map((s) => MasonryGridView.count(
scrollDirection: Axis.vertical,
// 展示几列
crossAxisCount: 2,
// 元素总个数
itemCount: _waterFallList.length,
// 单个子元素
itemBuilder: (context, index) {
return _waterCard(_waterFallList[index]);
},
// 纵向元素间距
mainAxisSpacing: 10,
// 横向元素间距
crossAxisSpacing: 10,
//本身不滚动,让外面的scrollview来滚动
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true, //收缩,让元素宽度自适应
))
.toList(),
),
),
));
}
//上拉加载函数
Future<void> _loadMoreData() async {
// 延迟1s增加数据
return Future.delayed(const Duration(seconds: 2), () {
if (mounted) {
for (int i = 0; i < 5; i++) {
_waterFallList.insert(
_waterFallList.length, _random.nextDouble() * 50);
}
setState(() {});
}
});
}
GestureDetector _buildFavouriteTypeItem(int index) => GestureDetector(
onTap: () {
_onTypeClick = index;
setState(() {});
},
onLongPress: () {},
child: Container(
alignment: Alignment.center,
child: Text("type$index",
style: TextStyle(
color: index == _onTypeClick ? Colors.orangeAccent : Colors.red,
shadows: const [
Shadow(
color: Colors.black,
offset: Offset(.5, .5),
blurRadius: 4)
])),
));
Container _buildFavouriteItem(Color color) => Container(
alignment: Alignment.center,
width: 100,
height: 30,
color: color,
child: const Text(
"FavoriteItem",
style: TextStyle(color: Colors.white, shadows: [
Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2)
]),
),
);
}
- StickyTabBarDelegate.dart
import 'package:flutter/material.dart';
class SliverTabBarDelegate extends SliverPersistentHeaderDelegate {
final TabBar widget;
final Color color;
const SliverTabBarDelegate(this.widget, {required this.color});
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: color,
child: widget,
);
}
@override
bool shouldRebuild(SliverTabBarDelegate oldDelegate) {
return false;
}
@override
double get maxExtent => widget.preferredSize.height;
@override
double get minExtent => widget.preferredSize.height;
}
main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'homePage.dart';
void main() {
runApp(const MyApp());
// SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(statusBarColor: Colors.transparent));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Main(title: 'Flutter'),
);
}
}
使用的技术
- 瀑布流MasonryGridView
- NestedScrollView+TabBarView
- 其余的基本就是基础控件