1、搭建flutter的开发环境:http://www.haijin.xyz/article/730
2、创建一个项目:
flutter create myapp
3、项目结构:
4、相关代码:
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 'package:provider/provider.dart';
import '../../vm/search_vm.dart';
import '../about_page.dart';
class AboutTabPage extends StatefulWidget {
final Data? data;
final SearchVm searchVm;
const AboutTabPage({
super.key,
required this.data,
required this.searchVm,
});
@override
_HomePageState createState() => _HomePageState(data: data,searchVm:searchVm);
}
class _HomePageState extends State<AboutTabPage> with AutomaticKeepAliveClientMixin {
final Data? data;
late final SearchVm searchVm;
_HomePageState({
required this.data,
required this.searchVm,
});
final ScrollController _scrollController = ScrollController();
// 索引
int _itemIndex = 0;
@override
void didUpdateWidget(covariant AboutTabPage oldWidget) {
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<SearchVm>(create: (content){
return searchVm;
},child: Consumer<SearchVm>(builder:(context,vm,child){
return Scaffold(
body: RefreshIndicator(
onRefresh: vm.refreshFunc, // 下拉刷新
child: vm.wallpaper == null
? Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
setState(() {
vm.currentPage = 1; // 重置页码
// _records.clear(); // 清空列表
});
vm.fetchData(data: data); // 获取第一页数据
},
child: Text('获取壁纸'),
),
],
),
) : ListView.builder(
itemCount: (vm.wallpaper?.pageSize??0) as int,
itemBuilder: (context, index) {
_itemIndex = index;
if(index > vm.currentPage * 10 - 5){
print("加载更多");
vm.currentPage++;
vm.fetchData(data: data);
}
if (index < vm.records!.length) {
final item = vm.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;
}
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';
import 'package:myapp/vm/search_vm.dart';
class AboutPage extends StatefulWidget {
const AboutPage({super.key});
@override
State<StatefulWidget> createState() {
return _AboutPageState();
}
}
class _AboutPageState extends State<AboutPage> with SingleTickerProviderStateMixin {
WallpaperTypeApi _wallpaperTypeApi = WallpaperTypeApi();
late TabController _tabController;
WallpaperType? _wallpaperType;
final List<Tab> _tabs = [];
bool _isDataLoaded = false; // 添加一个标志来跟踪数据是否加载完成
late List<Widget> _tabBarView = [];
String keyWorld = "";
final TextEditingController _textEditingController = TextEditingController();
final SearchVm _searchVm = SearchVm();
Future<void> _fetchData() async {
try{
dynamic data = await _wallpaperTypeApi.awaitFetchTodoData();
_wallpaperType = WallpaperType.fromJson(data);
print("获取类型的数据");
print(_wallpaperType?.message);
setState(() {
if (_wallpaperType?.code == 0) {
_tabs.add(Tab(text: "搜索"));
_wallpaperType?.data?.forEach((item){
_tabs.add(Tab(text: item.typeName));
});
}
_tabController = TabController(length: _tabs.length, vsync: this);
_tabController.addListener(_handleTabChange);
_tabBarView = getTabBarView();
_isDataLoaded = true;
});
} catch(e) {
print(e);
}
}
void _handleTabChange() {
// 当前选中的标签索引
final int currentIndex = _tabController.index;
print('Selected tab: $currentIndex');
// 在这里可以执行与当前选中标签相关的逻辑
}
@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: (){
keyWorld = _textEditingController.text;
_searchVm.setKeyWorld(keyWorld);
// 跳转到搜索页
_tabController.animateTo(0, duration: Duration(milliseconds: 300), curve: Curves.easeInOut);
},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 = [];
Data allData = Data(id: null,typeName: "所有");
tbs.add(AboutTabPage(data: allData,searchVm: _searchVm,));
_wallpaperType?.data?.forEach((item){
SearchVm s = SearchVm();
tbs.add(AboutTabPage(data: item,searchVm: s,));
});
return tbs??[];
}
Widget get _inputBox {
return Expanded(
child: TextField(
controller: _textEditingController,
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))),
),
);
;
}
}
search_vm.dart:
import 'package:flutter/foundation.dart';
import 'package:myapp/pages/about/about_tab_page.dart';
import '../api/wallpaper_detail_api.dart';
import '../model/wallpaper_detail.dart';
import '../model/wallpaper_type.dart';
class SearchVm with ChangeNotifier {
String keyWord = "";
Data? _data;
final WallpaperDetailApi _wallpaperDetailApi = WallpaperDetailApi();
WallpaperDetail? wallpaper;
List<Records> records = [];
// 页码
int currentPage = 1;
void setKeyWorld(String key){
keyWord = key;
print("搜索的关键词:$key");
currentPage = 1;
fetchData(data: _data);
}
Future<void> fetchData({Data? data}) async {
try{
print("页码数:");
print(currentPage);
_data = data;
var id = null;
if (data != null && data.id != null) {
id = data.id as int;
}
_wallpaperDetailApi.fetchTodoData(currentPage: currentPage,callBack: _analyzeWallpaperData,typeId: id,name: keyWord);
} catch(e) {
print(e);
}
}
// 拉下刷新函数
Future<void> refreshFunc({Data? data}) async {
print("刷新");
currentPage = 1;
fetchData(data: data);
}
// 解析获取的数据
void _analyzeWallpaperData(dynamic data) {
wallpaper = WallpaperDetail.fromJson(data);
if (currentPage == 1) {
// 如果是第一页,则替换列表
records = wallpaper!.records!;
} else {
// 如果不是第一页,则追加到列表末尾
wallpaper!.records?.forEach((item){
records.add(item);
});
}
notifyListeners();
print("改变数据");
}
}
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
provider: ^6.1.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
uses-material-design: true
assets:
- images/
状态管理现在依赖的组件就是provider。
过程:首先 在一个vm模型中 with ChangeNotifier
数据请求完之后可以notifyListeners();重新渲染ui,
ChangeNotifierProvider<SearchVm>(create: (content){
return searchVm;
},child: Consumer<SearchVm>(builder:(context,vm,child){
在需要重新渲染ui的地方使用ChangeNotifierProvider传入定义好的searchVm ,当searchVm数据发生改变的时候就会通知Consumer进行重新渲染。
这里的例子是一个搜索的功能。
运行截图: