从另一个小部件传递到重新渲染UI的参数中更新一个小部件
我有两个小部件是一个ListView Builder,它从REST API水平显示按钮列表,而另一个未来的构建器则根据按钮的类别名称显示了来自另一个REST API的图像。
按类别按钮确实将值传递给了我创建的Fetchphotos未来,我可以看到类别名称出现在调试控制台中。但是,尽管更改了变量值并在类别按钮上打开setState内部的fetchphoto,但UI永远不会重新呈现。
我在下面的类别清单中发表了评论,我认为我已经解决了,但无济于事 //设置状态以更新fetchphotos和重新渲染UI不起作用,
我该如何使用UI从Fetchphotos Future中使用新近获取的JSON数据刷新并添加了更新的QueryString参数?
Future<List<Category>> fetchCategories(http.Client client) async {
final response = await client
.get(Uri.parse('./categories'));
return compute(parseCategories, response.body);
}
List<Category> parseCategories(String responseBody) {
final parsed1 = jsonDecode(responseBody);
parsed1.insert(0, {"id": "0", "name": "All");
final parsed = parsed1.cast<Map<String, dynamic>>();
return parsed.map<Category>((json) => Category.fromJson(json)).toList();
}
class Category {
final String id;
final String name;
final int useCount;
const Category(
{required this.id, required this.name, required this.useCount});
factory Category.fromJson(Map<String, dynamic> json) {
return Category(
id: json['id'] as String,
name: json['name'] as String
);
}
}
class CategoryList extends StatefulWidget {
const CategoryList({Key? key, required this.categories}) : super(key: key);
final List<Category> categories;
@override
State<CategoryList> createState() => _CategoryListState();
}
var chosenCategory = "all";
class _CategoryListState extends State<CategoryList> {
@override
Widget build(BuildContext context) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: widget.categories.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(left: 2, right: 2),
child: ElevatedButton(
onPressed: () {
setState(() {
// SET STATE TO UPDATE fetchPhotos AND RE-RENDER UI NOT WORKING
chosenCategory = widget.categories[index].name;
fetchPhotos(http.Client(), chosenCategory);
});
},
child: Text(widget.categories[index].name),
));
},
);
}
}
Future<List<Photo>> fetchPhotos(http.Client client, chosenCategory) async {
print("---- CHOSEN CATEGORY ------ $chosenCategory");
print(chosenCategory);
final response = await client.get(Uri.parse(
' 'https://localhost/photoapp/getPhotos?searchString=$chosenCategory'));
'));
return compute(parsePhotos, response.body);
}
List<Photo> parsePhotos(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
class Photo {
final String id;
final String name;
final String imageUrl;
const Photo({
required this.id,
required this.name,
required this.imageUrl,
});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
id: json['id'] as String,
name: json['name'] as String,
imageUrl: json['imageUrl'] as String,
);
}
}
class PhotosList extends StatefulWidget {
const PhotosList({Key? key, required this.photos}) : super(key: key);
final List<Photo> photos;
@override
State<PhotosList> createState() => _PhotosListState();
}
class _PhotosListState extends State<PhotosList> {
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: widget.photos.length,
itemBuilder: (context, index) {
return Stack(
alignment: Alignment.topCenter,
children: <Widget>[
InkWell(
onTap: () { },
child: Container(
height: 300.0,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
alignment: FractionalOffset.center,
image: widget.photos[index].imageUrl,
),
),
),
)), // Container Image
Container(
margin: const EdgeInsets.only(top: 150),
width: MediaQuery.of(context).size.width * 0.92,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(widget.photos[index].name,
style: TextStyle(fontSize: 12, color: Colors.white),
maxLines: 1,
textAlign: TextAlign.center)),
decoration: BoxDecoration(
color: Colors.black,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.8),
spreadRadius: 1,
blurRadius: 3,
offset: const Offset(1, 1),
),
],
),
),
],
);
},
);
}
}
class AlbumPage extends StatefulWidget {
AlbumPage({Key, key, this.title}) : super(key: key);
final String? title;
@override
_AlbumState createState() => _ClbumState();
}
class _AlbumState extends State<AlbumPage> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => AlbumCubit(AlbumState.initial),
child: AlbumWidget(),
);
}
}
class AlbumWidget extends StatefulWidget {
@override
_AlbumWidgetState createState() => _AlbumWidgetState();
}
class _AlbumWidgetState extends State<AlbumWidget> {
@override
void initState() {
super.initState();
}
appBar() {
return AppBar(
title: const Text("Photo Album"),
leading: GestureDetector(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
));
}
getCategories() {
return Container(
padding: const EdgeInsets.all(14.0),
color: Colors.grey,
width: double.infinity,
height: 60,
child: FutureBuilder<List<Category>>(
future: fetchCategories(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred.'),
);
} else if (snapshot.hasData) {
return CategoryList(categories: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
getPhotos() {
return Flexible(
child: FutureBuilder<List<Photo>>(
future: fetchPhotos(http.Client(), chosenCategory),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred.'),
);
} else if (snapshot.hasData) {
return PhotosList(photos: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBar(),
body: Column(
children: <Widget>[
getCategories(),
getPhotos(),
],
));
}
}
I have two widgets one is a listview builder that displays a list of buttons horizontally from a rest API and the other a future builder that displays images from another rest API based on the category name of the button.
Pressing a category button does pass the value to the fetchPhotos future I have created and I can see the category name appear in the debug console. However, the UI never re-renders, despite changing the variable value and calling fetchPhoto inside setState onPressed on a category button.
I've made a comment in the categoryList below where I thought I had it solved but to no avail
// SET STATE TO UPDATE fetchPhotos AND RE-RENDER UI NOT WORKING
How can I go about getting the ui to refresh with the newly fetched JSON data from the fetchPhotos future with the updated querystring parameter added?
Future<List<Category>> fetchCategories(http.Client client) async {
final response = await client
.get(Uri.parse('./categories'));
return compute(parseCategories, response.body);
}
List<Category> parseCategories(String responseBody) {
final parsed1 = jsonDecode(responseBody);
parsed1.insert(0, {"id": "0", "name": "All");
final parsed = parsed1.cast<Map<String, dynamic>>();
return parsed.map<Category>((json) => Category.fromJson(json)).toList();
}
class Category {
final String id;
final String name;
final int useCount;
const Category(
{required this.id, required this.name, required this.useCount});
factory Category.fromJson(Map<String, dynamic> json) {
return Category(
id: json['id'] as String,
name: json['name'] as String
);
}
}
class CategoryList extends StatefulWidget {
const CategoryList({Key? key, required this.categories}) : super(key: key);
final List<Category> categories;
@override
State<CategoryList> createState() => _CategoryListState();
}
var chosenCategory = "all";
class _CategoryListState extends State<CategoryList> {
@override
Widget build(BuildContext context) {
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: widget.categories.length,
itemBuilder: (context, index) {
return Padding(
padding: EdgeInsets.only(left: 2, right: 2),
child: ElevatedButton(
onPressed: () {
setState(() {
// SET STATE TO UPDATE fetchPhotos AND RE-RENDER UI NOT WORKING
chosenCategory = widget.categories[index].name;
fetchPhotos(http.Client(), chosenCategory);
});
},
child: Text(widget.categories[index].name),
));
},
);
}
}
Future<List<Photo>> fetchPhotos(http.Client client, chosenCategory) async {
print("---- CHOSEN CATEGORY ------ $chosenCategory");
print(chosenCategory);
final response = await client.get(Uri.parse(
' 'https://localhost/photoapp/getPhotos?searchString=$chosenCategory'));
'));
return compute(parsePhotos, response.body);
}
List<Photo> parsePhotos(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
class Photo {
final String id;
final String name;
final String imageUrl;
const Photo({
required this.id,
required this.name,
required this.imageUrl,
});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
id: json['id'] as String,
name: json['name'] as String,
imageUrl: json['imageUrl'] as String,
);
}
}
class PhotosList extends StatefulWidget {
const PhotosList({Key? key, required this.photos}) : super(key: key);
final List<Photo> photos;
@override
State<PhotosList> createState() => _PhotosListState();
}
class _PhotosListState extends State<PhotosList> {
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: widget.photos.length,
itemBuilder: (context, index) {
return Stack(
alignment: Alignment.topCenter,
children: <Widget>[
InkWell(
onTap: () { },
child: Container(
height: 300.0,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
alignment: FractionalOffset.center,
image: widget.photos[index].imageUrl,
),
),
),
)), // Container Image
Container(
margin: const EdgeInsets.only(top: 150),
width: MediaQuery.of(context).size.width * 0.92,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(widget.photos[index].name,
style: TextStyle(fontSize: 12, color: Colors.white),
maxLines: 1,
textAlign: TextAlign.center)),
decoration: BoxDecoration(
color: Colors.black,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.8),
spreadRadius: 1,
blurRadius: 3,
offset: const Offset(1, 1),
),
],
),
),
],
);
},
);
}
}
class AlbumPage extends StatefulWidget {
AlbumPage({Key, key, this.title}) : super(key: key);
final String? title;
@override
_AlbumState createState() => _ClbumState();
}
class _AlbumState extends State<AlbumPage> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => AlbumCubit(AlbumState.initial),
child: AlbumWidget(),
);
}
}
class AlbumWidget extends StatefulWidget {
@override
_AlbumWidgetState createState() => _AlbumWidgetState();
}
class _AlbumWidgetState extends State<AlbumWidget> {
@override
void initState() {
super.initState();
}
appBar() {
return AppBar(
title: const Text("Photo Album"),
leading: GestureDetector(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
));
}
getCategories() {
return Container(
padding: const EdgeInsets.all(14.0),
color: Colors.grey,
width: double.infinity,
height: 60,
child: FutureBuilder<List<Category>>(
future: fetchCategories(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred.'),
);
} else if (snapshot.hasData) {
return CategoryList(categories: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
getPhotos() {
return Flexible(
child: FutureBuilder<List<Photo>>(
future: fetchPhotos(http.Client(), chosenCategory),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred.'),
);
} else if (snapshot.hasData) {
return PhotosList(photos: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBar(),
body: Column(
children: <Widget>[
getCategories(),
getPhotos(),
],
));
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论