我正在使用flutter_provider进行状态管理。我想从Api加载页面(statefulwidget)加载的某些项目。我在页面开始处显示了一个加载器,并且想要在提取项目后显示它们。
PlayList.dart-
class Playlist extends StatefulWidget {
@override
_PlaylistState createState() => _PlaylistState();
}
class _PlaylistState extends State<Playlist> {
var videosState;
@override
void initState() {
super.initState();
videosState = Provider.of<VideosProvider>(context);
videosState.fetchVideosList();
}
@override
Widget build(BuildContext context) {
var videos = videosState.playlist;
return Scaffold(
appBar: AppBar(
title: Text('My Videos'),
),
body: RefreshIndicator(
child: Container(
width: double.infinity,
height: double.infinity,
child: videos.length
? ListView.builder(
itemBuilder: (BuildContext context, index) {
return _videoListItem(context, index, videos, videosState);
},
itemCount: videos.length,
)
: Center(
child: CircularProgressIndicator(),
),
),
onRefresh: () => null,
),
);
}
}
我的提供者就是这样-
class VideosProvider with ChangeNotifier {
List _playlist;
int _currentVideoId;
get playlist => _playlist;
void setPlayList(videosList) {
_playlist = videosList;
}
Future fetchVideosList() async {
http.Response response =
await http.get("http://192.168.1.22:3000/videos-list/");
print(json.decode(response.body));
videos = json.decode(response.body)["data"];
setPlayList(videos);
return videos;
}
}
这将导致错误-
inheritFromWidgetOfExactType(_Provider<VideosProvider>) or inheritFromElement() was called before _PlaylistState.initState() completed.
这是playList类父级的构建方法,包装在changenotifier中,
Widget build(BuildContext context) {
return ChangeNotifierProvider<VideosProvider>(
builder: (BuildContext context) => VideosProvider(),
child: MaterialApp(
title: "My App",
home: new Playlist(),
),
);
}
因此,互联网上flutter_provider上的所有示例都显示了无状态窗口小部件上提供程序的用法,其中状态更改发生在用户交互(如按钮单击)上。没有关于如何在statefulWidget中使用提供程序的信息,也没有关于必须在页面加载时更新数据而没有任何交互的情况。
我知道针对这种情况的streambuilder和futurebuilder,但想了解如何使用flutter_provider做到这一点。如何使用提供者在initState(在pageload中)调用fetchVideosList?是否可以/应该使用statelessWidget处理这种情况?
答案 0 :(得分:2)
在使用Provider进行状态管理时,不需要使用StatefullWidget,那么如何在应用程序启动时调用ChangeNotifier的方法?
您可以简单地在ChangeNotifier的构造函数中执行此操作,这样,当您将VideoProvider()指向ChangeNotifierProvider Builder时,构造函数将在提供程序首次构造VideosProvider时被调用,因此:
PlayList.dart:
class Playlist extends StatelessWidget {
@override
Widget build(BuildContext context) {
final videosState = Provider.of<VideosProvider>(context);
var videos = videosState.playlist;
return Scaffold(
appBar: AppBar(
title: Text('My Videos'),
),
body: RefreshIndicator(
child: Container(
width: double.infinity,
height: double.infinity,
child: videos.length
? ListView.builder(
itemBuilder: (BuildContext context, index) {
return _videoListItem(context, index, videos, videosState);
},
itemCount: videos.length,
)
: Center(
child: CircularProgressIndicator(),
),
),
onRefresh: () => null,
),
);
} }
VideosProvider.dart:
class VideosProvider with ChangeNotifier {
VideosProvider(){
fetchVideosList();
}
List _playlist;
int _currentVideoId;
get playlist => _playlist;
void setPlayList(videosList) {
_playlist = videosList;
}
Future fetchVideosList() async {
http.Response response =
await http.get("http://192.168.1.22:3000/videos-list/");
print(json.decode(response.body));
videos = json.decode(response.body)["data"];
setPlayList(videos);
return videos;
}
}
答案 1 :(得分:0)
使用提供程序时,您无需使用StatefulWidget(如Flutter团队的教程State management
您可以使用以下教程来了解如何通过提供程序和程序获取数据。 StatelessWidget:Flutter StateManagement with Provider
答案 2 :(得分:0)
是否可以/应该使用无状态窗口小部件来处理这种情况?
答案是:否,不是
我是StatefulWidget + Provider的重度用户。我总是使用这种模式来显示一个包含字段的表单,该字段可用于将来的编辑或输入。
form_screen.dart
class Form extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<FormProvider>(
builder: (_) {
return FormProvider(id: ...); // Passing Provider to child widget
},
child: FormWidget(), // So Provider.of<FormProvider>(context) can be read here
);
}
}
class FormWidget extends StatefulWidget {
@override
_FormWidgetState createState() => _FormWidgetState();
}
class _FormWidgetState extends State<FormWidget> {
final _formKey = GlobalKey<FormState>();
// No need to override initState like your code
@override
Widget build(BuildContext context) {
var formState = Provider.of<FormProvider>(context) // access any provided data
return Form(
key: _formKey,
child: ....
);
}
}
FormProvider作为类,需要从API更新其最新值。因此,最初,它将请求一些URL并更新相应的值。
form_provider.dart
class FormProvider with ChangeNotifier {
DocumentModel document;
int id;
FormProvider({@required int id}) {
this.id = id;
initFormFields(); // will perform network request
}
void initFormFields() async {
Map results = initializeDataFromApi(id: id);
try {
document = DocumentModel.fromJson(results['data']);
} catch (e) {
// Handle Exceptions
}
notifyListeners(); // triggers FormWidget to re-execute build method for second time
}
PlayList.dart
class PlaylistScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<VideosProvider>(
builder: (_) {
return VideosProvider(); // execute construct method and fetchVideosList asynchronously
},
child: Playlist(),
);
}
}
class Playlist extends StatefulWidget {
@override
_PlaylistState createState() => _PlaylistState();
}
class _PlaylistState extends State<Playlist> {
final _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
// We *moved* this to build method
// videosState = Provider.of<VideosProvider>(context);
// We *moved* this to constructor method in provider
// videosState.fetchVideosList();
}
@override
Widget build(BuildContext context) {
// Moved from initState
var videosState = Provider.of<VideosProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text('My Videos'),
),
body: RefreshIndicator(
}
}
provider.dart
class VideosProvider with ChangeNotifier {
VideosProvider() {
// *moved* from Playlist.initState()
fetchVideosList(); // will perform network request
}
List _playlist;
int _currentVideoId;
get playlist => _playlist;
void setPlayList(videosList) {
_playlist = videosList;
}
Future fetchVideosList() async {
http.Response response =
await http.get("http://192.168.1.22:3000/videos-list/");
print(json.decode(response.body));
videos = json.decode(response.body)["data"];
setPlayList(videos);
// return videos; // no need to return
// We need to notify Playlist widget to rebuild itself for second time
notifyListeners(); // mandatory
}
}