Flutter 将内容包含到页面中
Flutter include content into a page
我实际上有一个正在运行的搜索栏(自动完成)。
当我 select 结果时,displaySnack() 正在工作,它显示一个 snackBar,但我想显示 testList() 的内容。
我的目标是了解如何启动另一个小部件,以便能够一次又一次地在页面上添加新的小部件。
我的最终目标是在获得 selected 值后,发出 http 请求,获取列表 return 并显示列表视图。
函数已执行(我可以在调试器中看到它)但没有显示任何内容..
(我是 Flutter 的新手,所以请尽可能解释你的回复:))
onSuggestionSelected : 是的,我知道它是无效的..
import 'package:drawer/src/share/snack_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import '../models/post_model.dart';
import '../services/http_service.dart';
// import 'package:http/http.dart' as http;
class PostsPage extends StatelessWidget {
final String title;
const PostsPage({Key? key, required this.title}) : super(key: key);
static Future<List<Post>> filterList(String value) async {
List<Post> list = await HttpService.fetchPosts();
return list.where(
(x) => x.title.toLowerCase().contains(value.toLowerCase())).toList();
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SafeArea(
child: Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post?>(
debounceDuration: Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Select the namespace...',
),
),
suggestionsCallback: filterList,
itemBuilder: (context, Post? suggestion) {
final user = suggestion!;
return ListTile(
title: Text(user.title),
);
},
noItemsFoundBuilder: (context) => Container(
height: 100,
child: Center(
child: Text(
'No Namespace Found.',
style: TextStyle(fontSize: 24),
),
),
),
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
testList(context); ################################ HERE
},
),
),
),
);
}
Widget testList(BuildContext context) {
return ListView.separated(
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemCount: 2,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text("ppp"),
subtitle: Text("ppp"),
leading: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1547721064-da6cfb341d50"))
));
});
}
我需要那个:https://prnt.sc/136njev
所以你所做的基本上只是用你的 testList()
函数调用创建一个 ListView
并且什么都不做,但你想要做的是让那个小部件显示在屏幕,对吧?
如果您创建一个新的 Widget,Flutter 不会只显示它,您必须告诉它进行渲染。试想一下,你正在准备 Widgets(例如 Widgets 中的 Widgets),而 Flutter 会在你没有完成的情况下立即将它渲染到屏幕上,那就不会那么好。
您需要将该小部件推送到 Flutter 为您提供的 Navigator
小部件上。
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => testList(context)),
);
}
我建议您阅读这篇文章以 Navigation Basics。
您可以使用 listView 生成器来显示选定的结果。
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
//get results
var results = fetchResult(suggestion);
//return a listview of the results
return ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: results.length,
itemBuilder: (_context, index) {
Post post = results[index];
return Card(
elevation: 2,
child: InkWell(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
border: Border.all(color: Colors.black),
),
child: DefaultTextStyle(
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.white),
child: Row(children: [
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(post.data),
],
),
),
]),
),
),
onTap: () {
//do something when user clicks on a result
},
));
}
},
如果要显示 selected 项目的列表,则必须在小部件树中添加 ListView
。还要使用 StatefullWidget
而不是 StatelessWidget
,因为每当你 select 一个项目时,selected 列表就会改变状态。
状态示例代码
List<Post> selectedPosts;
@override
void initState() {
super.initState();
selectedPosts = [];
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post?>(
debounceDuration: Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Select the namespace...',
),
),
suggestionsCallback: filterList,
itemBuilder: (context, Post? suggestion) {
final user = suggestion!;
return ListTile(
title: Text(user.title),
);
},
noItemsFoundBuilder: (context) => Container(
height: 100,
child: Center(
child: Text(
'No Namespace Found.',
style: TextStyle(fontSize: 24),
),
),
),
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
setState(()=> selectedPosts.add(suggestion));
},
),
),
testList(context),
],
),
),
);
并在 testList
函数中更改 itemcount
itemCount: 2,
到
itemCount: selectedPosts?.length ?? 0,
很明显,您希望小部件重建以显示结果。最直接的方法是使用StatefulWidget。所以我在你的情况下使用它(你也可以找到很多方法来管理状态List of state management approaches)
- 将您的 PostsPage 更改为 StatefulWidget 并在选择用户时重建
- 在您的 PostsPage 中添加一个 Column 并分成两部分:TypeAheadField 和 Result
- 结果部分可以使用FutureBuilder(数据未准备好时可以显示loading indicator)
帖子页面:
class PostsPage extends StatefulWidget {
final String title;
const PostsPage({Key? key, required this.title}) : super(key: key);
static Future<List<Post>> filterList(String value) async {
// skip
}
@override
_PostsPageState createState() => _PostsPageState();
}
class _PostsPageState extends State<PostsPage> {
Post? user;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post>(
// ...
// skip
// ...
onSuggestionSelected: (Post? suggestion) {
setState(() {
user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
});
},
),
),
Expanded(child: MyResult(post: user)),
],
),
),
);
}
}
结果部分:
(我把它做成一个孤立的StatelessWidget只是为了更好的阅读。你可以使用原来的方法来构建widget)
class MyResult extends StatelessWidget {
const MyResult({
required this.post,
Key? key,
}) : super(key: key);
final Post? post;
Future<List<OtherObject>> getOtherObjects(Post? post) async{
if(post == null){
return [];
}else{
return Future.delayed(Duration(seconds:3),()=>[OtherObject(title: '001'),OtherObject(title: '002'),OtherObject(title: '003')]);
}
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<OtherObject>>(
future: getOtherObjects(post),
builder: (context, snapshot) {
if(snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
final result = snapshot.data!;
return ListView.separated(
separatorBuilder: (BuildContext context,
int index) => const Divider(),
itemCount: result.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(result[index].title),
subtitle: Text("ppp"),
leading: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1547721064-da6cfb341d50"),
),
),
);
},
);
}else {
return Center(child: CircularProgressIndicator());
}
}
);
}
}
我实际上有一个正在运行的搜索栏(自动完成)。
当我 select 结果时,displaySnack() 正在工作,它显示一个 snackBar,但我想显示 testList() 的内容。
我的目标是了解如何启动另一个小部件,以便能够一次又一次地在页面上添加新的小部件。
我的最终目标是在获得 selected 值后,发出 http 请求,获取列表 return 并显示列表视图。
函数已执行(我可以在调试器中看到它)但没有显示任何内容..
(我是 Flutter 的新手,所以请尽可能解释你的回复:)) onSuggestionSelected : 是的,我知道它是无效的..
import 'package:drawer/src/share/snack_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import '../models/post_model.dart';
import '../services/http_service.dart';
// import 'package:http/http.dart' as http;
class PostsPage extends StatelessWidget {
final String title;
const PostsPage({Key? key, required this.title}) : super(key: key);
static Future<List<Post>> filterList(String value) async {
List<Post> list = await HttpService.fetchPosts();
return list.where(
(x) => x.title.toLowerCase().contains(value.toLowerCase())).toList();
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SafeArea(
child: Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post?>(
debounceDuration: Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Select the namespace...',
),
),
suggestionsCallback: filterList,
itemBuilder: (context, Post? suggestion) {
final user = suggestion!;
return ListTile(
title: Text(user.title),
);
},
noItemsFoundBuilder: (context) => Container(
height: 100,
child: Center(
child: Text(
'No Namespace Found.',
style: TextStyle(fontSize: 24),
),
),
),
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
testList(context); ################################ HERE
},
),
),
),
);
}
Widget testList(BuildContext context) {
return ListView.separated(
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemCount: 2,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text("ppp"),
subtitle: Text("ppp"),
leading: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1547721064-da6cfb341d50"))
));
});
}
我需要那个:https://prnt.sc/136njev
所以你所做的基本上只是用你的 testList()
函数调用创建一个 ListView
并且什么都不做,但你想要做的是让那个小部件显示在屏幕,对吧?
如果您创建一个新的 Widget,Flutter 不会只显示它,您必须告诉它进行渲染。试想一下,你正在准备 Widgets(例如 Widgets 中的 Widgets),而 Flutter 会在你没有完成的情况下立即将它渲染到屏幕上,那就不会那么好。
您需要将该小部件推送到 Flutter 为您提供的 Navigator
小部件上。
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => testList(context)),
);
}
我建议您阅读这篇文章以 Navigation Basics。
您可以使用 listView 生成器来显示选定的结果。
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
//get results
var results = fetchResult(suggestion);
//return a listview of the results
return ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: results.length,
itemBuilder: (_context, index) {
Post post = results[index];
return Card(
elevation: 2,
child: InkWell(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
border: Border.all(color: Colors.black),
),
child: DefaultTextStyle(
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.white),
child: Row(children: [
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(post.data),
],
),
),
]),
),
),
onTap: () {
//do something when user clicks on a result
},
));
}
},
如果要显示 selected 项目的列表,则必须在小部件树中添加 ListView
。还要使用 StatefullWidget
而不是 StatelessWidget
,因为每当你 select 一个项目时,selected 列表就会改变状态。
状态示例代码
List<Post> selectedPosts;
@override
void initState() {
super.initState();
selectedPosts = [];
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post?>(
debounceDuration: Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Select the namespace...',
),
),
suggestionsCallback: filterList,
itemBuilder: (context, Post? suggestion) {
final user = suggestion!;
return ListTile(
title: Text(user.title),
);
},
noItemsFoundBuilder: (context) => Container(
height: 100,
child: Center(
child: Text(
'No Namespace Found.',
style: TextStyle(fontSize: 24),
),
),
),
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
setState(()=> selectedPosts.add(suggestion));
},
),
),
testList(context),
],
),
),
);
并在 testList
函数中更改 itemcount
itemCount: 2,
到
itemCount: selectedPosts?.length ?? 0,
很明显,您希望小部件重建以显示结果。最直接的方法是使用StatefulWidget。所以我在你的情况下使用它(你也可以找到很多方法来管理状态List of state management approaches)
- 将您的 PostsPage 更改为 StatefulWidget 并在选择用户时重建
- 在您的 PostsPage 中添加一个 Column 并分成两部分:TypeAheadField 和 Result
- 结果部分可以使用FutureBuilder(数据未准备好时可以显示loading indicator)
帖子页面:
class PostsPage extends StatefulWidget {
final String title;
const PostsPage({Key? key, required this.title}) : super(key: key);
static Future<List<Post>> filterList(String value) async {
// skip
}
@override
_PostsPageState createState() => _PostsPageState();
}
class _PostsPageState extends State<PostsPage> {
Post? user;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post>(
// ...
// skip
// ...
onSuggestionSelected: (Post? suggestion) {
setState(() {
user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
});
},
),
),
Expanded(child: MyResult(post: user)),
],
),
),
);
}
}
结果部分: (我把它做成一个孤立的StatelessWidget只是为了更好的阅读。你可以使用原来的方法来构建widget)
class MyResult extends StatelessWidget {
const MyResult({
required this.post,
Key? key,
}) : super(key: key);
final Post? post;
Future<List<OtherObject>> getOtherObjects(Post? post) async{
if(post == null){
return [];
}else{
return Future.delayed(Duration(seconds:3),()=>[OtherObject(title: '001'),OtherObject(title: '002'),OtherObject(title: '003')]);
}
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<OtherObject>>(
future: getOtherObjects(post),
builder: (context, snapshot) {
if(snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
final result = snapshot.data!;
return ListView.separated(
separatorBuilder: (BuildContext context,
int index) => const Divider(),
itemCount: result.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(result[index].title),
subtitle: Text("ppp"),
leading: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1547721064-da6cfb341d50"),
),
),
);
},
);
}else {
return Center(child: CircularProgressIndicator());
}
}
);
}
}