1、搭建flutter的开发环境:http://www.haijin.xyz/article/730
2、创建一个项目:
flutter create myapp
3、项目结构:
4、相关代码:
静态资源: images/search.png
wallpaper_type_api.dart:
import '../network/api_service.dart';
class WallpaperTypeApi {
final ApiService _apiService = ApiService();
void fetchTodoData({
required Function(dynamic data) callBack,
}) {
_apiService.get(
url: 'http://192.168.97.13:5800/api/type',
body: null,
callbackFunc:callBack,
);
}
dynamic awaitFetchTodoData() async {
var data = await _apiService.awaitGet(
url: 'http://192.168.97.13:5800/api/type',
body: null,
);
return data;
}
}
wallpaper_type.dart:
/// code : 0
/// message : "成功"
/// data : [{"id":1,"typeName":"手机壁纸"},{"id":2,"typeName":"植物"},{"id":3,"typeName":"女性"},{"id":4,"typeName":"动漫"},{"id":5,"typeName":"食物"},{"id":6,"typeName":"影视"},{"id":7,"typeName":"非主流"},{"id":8,"typeName":"明星"},{"id":9,"typeName":"军事"},{"id":10,"typeName":"游戏"},{"id":11,"typeName":"汽车"},{"id":12,"typeName":"爱情"},{"id":13,"typeName":"体育"},{"id":14,"typeName":"搞笑"},{"id":15,"typeName":"动物"},{"id":16,"typeName":"风景"},{"id":17,"typeName":"节日"},{"id":18,"typeName":"标志"},{"id":19,"typeName":"另类"},{"id":20,"typeName":"动态"},{"id":21,"typeName":"其他1"},{"id":22,"typeName":"其他2"}]
class WallpaperType {
WallpaperType({
num? code,
String? message,
List<Data>? data,
}) {
_code = code;
_message = message;
_data = data;
}
WallpaperType.fromJson(dynamic json) {
_code = json['code'];
_message = json['message'];
if (json['data'] != null) {
_data = [];
json['data'].forEach((v) {
_data?.add(Data.fromJson(v));
});
}
}
num? _code;
String? _message;
List<Data>? _data;
WallpaperType copyWith({
num? code,
String? message,
List<Data>? data,
}) =>
WallpaperType(
code: code ?? _code,
message: message ?? _message,
data: data ?? _data,
);
num? get code => _code;
String? get message => _message;
List<Data>? get data => _data;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['code'] = _code;
map['message'] = _message;
final _data = this._data;
if (_data != null) {
map['data'] = _data.map((v) => v.toJson()).toList();
}
return map;
}
}
/// id : 1
/// typeName : "手机壁纸"
class Data {
Data({
num? id,
String? typeName,
}) {
_id = id;
_typeName = typeName;
}
Data.fromJson(dynamic json) {
_id = json['id'];
_typeName = json['typeName'];
}
num? _id;
String? _typeName;
Data copyWith({
num? id,
String? typeName,
}) =>
Data(
id: id ?? _id,
typeName: typeName ?? _typeName,
);
num? get id => _id;
String? get typeName => _typeName;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = _id;
map['typeName'] = _typeName;
return map;
}
}
about_tab_page.dart:
import 'package:flutter/material.dart';
import 'package:myapp/api/wallpaper_detail_api.dart';
import 'package:myapp/model/wallpaper_detail.dart';
import 'package:myapp/model/wallpaper_type.dart';
import 'package:myapp/pages/detail_page.dart';
import 'package:myapp/route/routes.dart';
import '../about_page.dart';
class AboutTabPage extends StatefulWidget {
final Data? data;
final SearchQuery searchQuery;
const AboutTabPage({
super.key,
required this.data,
required this.searchQuery,
});
@override
_HomePageState createState() => _HomePageState(data: data);
}
class _HomePageState extends State<AboutTabPage> with AutomaticKeepAliveClientMixin {
late final Data? data;
late final SearchQuery searchQuery;
_HomePageState({
required this.data,
});
final WallpaperDetailApi _wallpaperDetailApi = WallpaperDetailApi();
WallpaperDetail? _wallpaper;
List<Records> _records = [];
// 页码
int _currentPage = 1;
// 是否正在刷新
bool _isRefreshing = false;
// 是否正在加载更多
bool _isLoadingMore = false;
double _listHeight = 0.0; // 列表的高度
final ScrollController _scrollController = ScrollController();
// 索引
int _itemIndex = 0;
@override
void didUpdateWidget(covariant AboutTabPage oldWidget) {
super.didUpdateWidget(oldWidget);
searchQuery = oldWidget.searchQuery;
}
@override
void initState() {
// TODO: implement initState
super.initState();
_scrollController.addListener((){
// 滑动到底部,去做加载更多的请求
print("_itemIndex:$_itemIndex");
print(_records.length);
if(_itemIndex + 5 > _records.length){
if (!_isLoadingMore) {
print("加载更多");
_currentPage++;
_fetchData();
}
}
});
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
Future<void> _fetchData() async {
try{
print("页码数:");
print(_currentPage);
_wallpaperDetailApi.fetchTodoData(currentPage: _currentPage,callBack: _analyzeWallpaperData,typeId: (data?.id??1) as int);
} catch(e) {
print(e);
}
}
// 拉下刷新函数
Future<void> _refreshFunc() async {
print("刷新");
_currentPage = 1;
_fetchData();
}
// 解析获取的数据
void _analyzeWallpaperData(dynamic data) {
_wallpaper = WallpaperDetail.fromJson(data);
print("获取的数据");
if (_currentPage == 1) {
// 如果是第一页,则替换列表
setState(() {
_records = _wallpaper!.records!;
});
} else {
// 如果不是第一页,则追加到列表末尾
setState(() {
_wallpaper!.records?.forEach((item){
_records.add(item);
});
});
}
setState(() {
_isRefreshing = false;
_isLoadingMore = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: RefreshIndicator(
onRefresh: _refreshFunc, // 下拉刷新
child: _wallpaper == null
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
setState(() {
_currentPage = 1; // 重置页码
// _records.clear(); // 清空列表
});
_fetchData(); // 获取第一页数据
},
child: Text('获取壁纸'),
),
],
),
) : ListView.builder(
itemCount: (_wallpaper?.pageSize??0) as int,
itemBuilder: (context, index) {
_itemIndex = index;
if(index > _currentPage * 10 - 5){
if (!_isLoadingMore) {
print("加载更多");
_currentPage++;
_fetchData();
}
}
if (index < _records!.length) {
final item = _records?[index];
return ListTile(
leading: Image.network(item!.wallpaperListUrl??""),
title: Text(item!.wallpaperName??""),
subtitle: Text('ID: ${item?.id}'),
onTap: () {
print("跳转到壁纸的详情页面");
Navigator.pushNamed(context, RoutePath.detailPage,
arguments:item
);
// Navigator.push(context, MaterialPageRoute(builder: (context){
// return DetailPage(
// records: item,
// );
// }));
},
);
} else {
return Container(
height: 50,
alignment: Alignment.center,
child: CircularProgressIndicator(),
);
}
},
// controller: _scrollController,
),
),
);
}
@override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
abstract class SearchQuery {
String searchFunc();
}
about_page.dart:
// lib/pages/about_page.dart
import 'package:flutter/material.dart';
import 'package:myapp/api/wallpaper_type_api.dart';
import 'package:myapp/model/wallpaper_type.dart';
import 'package:myapp/pages/about/about_tab_page.dart';
class AboutPage extends StatefulWidget {
const AboutPage({super.key});
@override
State<StatefulWidget> createState() {
return _AboutPageState();
}
}
class _AboutPageState extends State<AboutPage> with SingleTickerProviderStateMixin implements SearchQuery {
WallpaperTypeApi _wallpaperTypeApi = WallpaperTypeApi();
late TabController _tabController;
WallpaperType? _wallpaperType;
final List<Tab> _tabs = [];
bool _isDataLoaded = false; // 添加一个标志来跟踪数据是否加载完成
late List<Widget> _tabBarView = [];
String keyWorld = "";
Future<void> _fetchData() async {
try{
dynamic data = await _wallpaperTypeApi.awaitFetchTodoData();
_wallpaperType = WallpaperType.fromJson(data);
print("获取类型的数据");
print(_wallpaperType?.message);
setState(() {
if (_wallpaperType?.code == 0) {
_wallpaperType?.data?.forEach((item){
_tabs.add(Tab(text: item.typeName));
});
}
_tabController = TabController(length: _tabs.length, vsync: this);
_tabBarView = getTabBarView();
_isDataLoaded = true;
});
} catch(e) {
print(e);
}
}
@override
void initState() {
super.initState();
_fetchData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Container(
height: 45,
decoration: BoxDecoration(
border: Border(
top: BorderSide(width: 0.5,color: Colors.grey),
bottom: BorderSide(width: 0.5,color: Colors.grey),
)
),
padding: EdgeInsets.only(left:20,right: 20),
child: Row(children: [
// Text("搜索热词",style: TextStyle(fontSize: 14,color: Colors.black),),
Container(child: _inputBox),
Expanded(child: SizedBox()),
GestureDetector(onTap: (){
setState(() {
print("搜索");
});
},child: Image.asset("images/search.png",width: 30,height: 30,),),
],),
),
bottom: _isDataLoaded ? TabBar( // 使用 _isDataLoaded 标志来决定是否显示 TabBar
controller: _tabController,
tabs: _tabs,
labelColor: Colors.blue,
indicatorColor: Colors.blue,
isScrollable: true,
) : null,
),
body: SafeArea(child:
_isDataLoaded ? TabBarView(controller: _tabController,children: _tabBarView,) : const Center(child: Text("关于页面"),),
),
);
}
List<Widget> getTabBarView() {
List<Widget>? tbs = _wallpaperType?.data?.map((item){
return AboutTabPage(data: item,searchQuery: this,);
}).cast<Widget>().toList();
return tbs??[];
}
Widget get _inputBox {
return Expanded(
child: TextField(
style: const TextStyle(
fontSize: 18.0, color: Colors.black, fontWeight: FontWeight.w300),
decoration: InputDecoration(
// contentPadding: EdgeInsets.fromLTRB(1, 3, 1, 3),
// contentPadding: EdgeInsets.only(bottom: 0),
contentPadding:
const EdgeInsets.symmetric(vertical: 0, horizontal: 12),
border: InputBorder.none,
hintText: "搜你想搜的",
hintStyle: TextStyle(fontSize: 15),
enabledBorder: const OutlineInputBorder(
// borderSide: BorderSide(color: Color(0xFFDCDFE6)),
borderSide: BorderSide(color: Colors.transparent),
borderRadius: BorderRadius.all(Radius.circular(4.0)),
),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(8)),
borderSide: BorderSide(color: Colors.transparent))),
),
);
;
}
@override
String searchFunc() {
return keyWorld;
}
}
pubspec.yaml:
name: myapp
description: "A new Flutter project."
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: '>=3.4.4 <4.0.0'
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.6
dio: ^4.0.6
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
uses-material-design: true
assets:
- images/
其他的代码可以参考其他的文章。搜索的功能还没实现,还没看状态管理的内容,暂时还不知道tab和tabview之间怎么联动。