How to add CircularProgressIndicator in flutter? - flutter

I have call three web services in one page now for that i want show only one CircularProgressIndicator.How to show CircularProgressIndicator first when i open screen after CircularProgressIndicator show all UI.

Use Future.wait to merge three futures (web service calls) then use FutureBuilder to show CircularProgressIndicator while waiting for the merged Future to complete.
Try it like this,
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String _data1;
String _data2;
String _data3;
Future<void> _webCall;
#override
void initState() {
_webCall = _callWebServices();
super.initState();
}
Future<bool> _callWebServices() async {
await Future.wait([_webService1(), _webService2(), _webService3()]);
return true;
}
Future<void> _webService1() async {
await Future.delayed(const Duration(seconds: 1)); //TODO: do webservice call `get` or `post`
_data1 = "This is data1"; //TODO: save the data
}
Future<void> _webService2() async {
await Future.delayed(const Duration(seconds: 5)); //TODO: do webservice call `get` or `post`
_data2 = "This is data2"; //TODO: save the data
}
Future<void> _webService3() async {
await Future.delayed(const Duration(seconds: 3)); //TODO: do webservice call `get` or `post`
_data3 = "This is data3"; //TODO: save the data
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Demo"),
),
body: FutureBuilder(
future: _webCall,
builder: (context, snapshot) {
print(snapshot);
if (snapshot.connectionState == ConnectionState.waiting)
return _buildProgressIndicator();
else if (snapshot.hasError)
return _buildError();
else
return _buildBody();
},
),
);
}
Widget _buildProgressIndicator() {
return Center(
child: CircularProgressIndicator(),
);
}
Widget _buildBody() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_data1),
Text(_data2),
Text(_data3),
],
),
);
}
Widget _buildError() {
return Center(
child: Text("Error while loading Web Services"),
);
}
}

Related

How to read file from file to flutter?

I have code that I use to write data to a file (such as global settings for an application). I write down the data and see. But the problem is that I do not understand how to take these mini data from another page. For example I wrote the word "Test" and I want and I want to assign that word to some variable in another page. I will be grateful for your help. Here is my code:
import 'dart:io';
import 'dart:async';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Reading and Writing to Storage",
home: Home(
storage: Storage(),
),
);
}
}
class Home extends StatefulWidget {
final Storage storage;
Home({Key key, #required this.storage}) : super(key: key);
#override
HomeState createState() => HomeState();
}
class HomeState extends State<Home> {
TextEditingController controller = TextEditingController();
String state;
Future<Directory> _appDocDir;
#override
void initState() {
super.initState();
widget.storage.readData().then((String value) {
setState(() {
state = value;
});
});
}
Future<File> writeData() async {
setState(() {
state = controller.text;
controller.text = '';
});
return widget.storage.writeData(state);
}
void getAppDirectory() {
setState(() {
_appDocDir = getApplicationDocumentsDirectory();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Reading and Writing Files'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('${state ?? "File is Empty"}'),
TextField(
controller: controller,
),
RaisedButton(
onPressed: writeData,
child: Text('Write to File'),
),
RaisedButton(
child: Text("Get DIR path"),
onPressed: getAppDirectory,
),
FutureBuilder<Directory>(
future: _appDocDir,
builder:
(BuildContext context, AsyncSnapshot<Directory> snapshot) {
Text text = Text('');
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
text = Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) {
text = Text('Path: ${snapshot.data.path}');
} else {
text = Text('Unavailable');
}
}
return new Container(
child: text,
);
},
)
],
),
),
);
}
}
class Storage {
Future<String> get localPath async {
final dir = await getApplicationDocumentsDirectory();
return dir.path;
}
Future<File> get localFile async {
final path = await localPath;
return File('$path/db.txt');
}
Future<String> readData() async {
try {
final file = await localFile;
String body = await file.readAsString();
return body;
} catch (e) {
return e.toString();
}
}
Future<File> writeData(String data) async {
final file = await localFile;
return file.writeAsString("$data");
}
}
And scrin:
You can pass the value to other page using Navigator and parameter.
class NewScreen extends StatelessWidget {
final String data;
NewScreen({this.data});
...
}
When you move to 'NewScreen' by using Navigator at your 'Home' page,
pass the data what you transfer.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewScreen(data: 'Test'),
),
);

How to add a CircularProgressIndicator while navigating to a PDF View Page

In the code below, I am reading a pdf file from my images folder then viewing it in a new page. But when I navigate to the new page it takes time to load the pdf. So, I want to add a CircularProgressIndicator while transitioning. I can't figure out how to do it. How do I add a CircularProgressIndicator widget?
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_full_pdf_viewer/flutter_full_pdf_viewer.dart';
class PdfPage extends StatefulWidget {
#override
_PdfPageState createState() => _PdfPageState();
}
class _PdfPageState extends State<PdfPage> {
Future<String> makeFile() async {
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
File tempFile = File('$tempPath/copy.pdf');
ByteData bd = await rootBundle.load('images/dummy.pdf');
await tempFile.writeAsBytes(bd.buffer.asUint8List(), flush: true);
return tempFile.path;
}
bool isLoading = false;
#override
void initState() {
// TODO: implement initState
super.initState();
makeFile().then((value) {
setState(() {
path = value;
print(path);
//isLoading = false;
});
});
}
String path;
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: RaisedButton(
child: Text("Open PDF Screen"),
onPressed: () {
// CircularProgressIndicator(value: true,);
Navigator.push(context,
MaterialPageRoute(builder: (context) => PDFScreen(path)));
},
),
),
);
}
}
class PDFScreen extends StatelessWidget {
final String path;
// final bool isLoading;
PDFScreen(
this.path,
);
#override
Widget build(BuildContext context) {
return PDFViewerScaffold(
appBar: AppBar(
title: Text("Scaffold PDF"),
),
path: path);
}
}
You can use FutureBuilder class which is precisely made for this. That is, some action is expected in the future and you would like to show a progress indicator meanwhile. The action may actually complete or return an error. Futurebuilder lets you handle all these scenarios.
You can shift makefile() into class PDFScreen and use widget build method to return the FutureBuilder. The simplest implementation is below.
#override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: makeFile(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return CircularProgressIndicator();
} else {
return PDFViewerScaffold(
appBar: AppBar(
title: Text("Scaffold PDF"),
),
path: snapshot.data
);
}
},
);
}
You might want to visit the official docuementation or this blog on Medium.
One way is to use it in an Dialog.
static Future showDialog(BuildContext context, GlobalKey _key) async {
return showLoadingDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context){
return SimpleDialog(
key: _key,
children: <Widget>[
Center(
child: Container(
child: Row(
children: <Widget>[
CircularProgressIndicator(),
SizedBox(
height:10,
width:10,
),
Text("Please Wait!"),
]
),
],
),
);
}
);
}
Here GlobalKey is used to Show or hide the dialog created.
To call this simple initialize a GlobalKey in the class as GlobalKey<State> _dialogKey = GlobalKey<State>();.
Then you can show the dialog as:
showLoadingDialog(context, _dialogKey);
And when you want it to hide, just call:
Navigator.of(_dialogKey.currentContext,rootNavigator: true).pop();
This is just one way of using CircularProgressIndicator in an SimpleDialog, Hope this helps.

Why this code is not showing CircularProgressIndicator?

This is a simple code which display a different Text based in a random number. I want to show the CircularProgressIndicator when the user push 'next' button and the method 'getRandom' delays 5 secs.
CircularProgressIndicator never is shown...why?
import 'package:flutter/material.dart';
import 'dart:math';
void main() {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
Future<String> random;
#override
void initState() {
super.initState();
random = getRandom();
}
Future<String> getRandom() async{
print("getRandom");
Future.delayed(const Duration(seconds: 5));
return "the number is"+Random().nextInt(100).toString();
}
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: AppBar(title: Text("Random Widget")),
body: Center(child:
FutureBuilder(
future:random,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(children: [
Text(snapshot.data,textScaleFactor: 4),
getNextButton()
]);
} else if (snapshot.hasError) {
return Text("ERROR");
}
return CircularProgressIndicator();
}
)
)),
);
}
Widget getNextButton(){
return RaisedButton(
child: Text("NEXT"),
color: Colors.red,
onPressed: () {
setState(() {
random=getRandom();
});
}
);
}
}
Thanks in advance!!
There are several mistakes in your code.
You are passing initialData. So the snapshot.hasData will be true in the beginning itself.
You didn't initialise the random for the first time.
You have to await your Future.delay
Change getRandom implementation:
Future<String> getRandom() async{
await Future.delayed(const Duration(seconds: 5));
return "the number is"+ Random().nextInt(100).toString();
}
Implement initState to initialise random future:
#override
void initState() {
super.initState();
random = getRandom();
}
Change future builder:
FutureBuilder(
future:random,
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
return Column(children: [
Text(snapshot.data,textScaleFactor: 4),
getNextButton()
]);
} else if (snapshot.hasError) {
return Text("ERROR");
}
return CircularProgressIndicator();
}
)
If your not using your initialData field in you FutureBuilder than delete and your code will work. If you for some reason need that value add the following statement to the end:
if(snapshot.data != 'starting') {
return CircularProgressIndicator();
}
See the docs for an example of how to use it

How to use flutter provider in a statefulWidget?

I am using flutter_provider for state management. I want to load some items on page(statefulwidget) load from Api. I am showing a loader on page start and want to show the items once they are fetched.
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,
),
);
}
}
My provider is like this -
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;
}
}
This gives an error of -
inheritFromWidgetOfExactType(_Provider<VideosProvider>) or inheritFromElement() was called before _PlaylistState.initState() completed.
here is the build method of the parent of playList class, wrapped in a changenotifier,
Widget build(BuildContext context) {
return ChangeNotifierProvider<VideosProvider>(
builder: (BuildContext context) => VideosProvider(),
child: MaterialApp(
title: "My App",
home: new Playlist(),
),
);
}
So, all the examples on flutter_provider on internet show usage of provider on a statelesswidget, where state changes occur on user interactions like a button click. None about how to use provider in a statefulWidget, and cases where data has to be updated on page load without any interaction.
I am aware of streambuilder and futurebuilder for this kind of scenarios, but want to understand how this can be done with flutter_provider. How can I use provider to call fetchVideosList in initState(on pageload)? Does this case can/should be handled with a statelessWidget?
Does this case can/should be handled with a statelessWidget?
The answer is : No, it does not
I am heavy user of StatefulWidget + Provider. I always use this pattern for displaying a Form which contains fields, that available for future edit or input.
Updated : February 9 2020
Regarding to Maks comment, I shared better way to manage provider using didChangeDependencies.
You may check to this github repository
main.dart
First Step
Initiate PlayListScreen inside ChangeNotifierProvider
class PlaylistScreenProvider extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<VideosProvider>(
create: (_) {
return VideosProvider();
},
child: PlaylistScreen(),
);
}
}
class MainScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Screen'),
),
body: Center(
child: RaisedButton(
child: Text("Go To StatefulWidget Screen"),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) {
return PlaylistScreenProvider();
},
),
);
},
),
),
);
}
}
Second Step
Make PlaylistScreen as Stateful Widget to hold TextEditingContoller
and other values.
playlistScreen.dart
class PlaylistScreen extends StatefulWidget {
#override
_PlaylistScreenState createState() => _PlaylistScreenState();
}
class _PlaylistScreenState extends State<PlaylistScreen> {
List _playlistList;
String _errorMessage;
Stage _stage;
final _searchTextCtrl = TextEditingController();
#override
void dispose() {
super.dispose();
_searchTextCtrl.dispose();
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
final videosState = Provider.of<VideosProvider>(context);
_playlistList = videosState.playlist;
_stage = videosState.stage;
_errorMessage = videosState.errorMessage;
}
void actionSearch() {
String text = _searchTextCtrl.value.text;
Provider.of<VideosProvider>(context, listen: false)
.updateCurrentVideoId(text);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Videos'),
),
body: Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: <Widget>[
Container(
child: RaisedButton.icon(
icon: Icon(Icons.search),
label: Text("Filter"),
onPressed: () {
actionSearch();
},
),
),
Container(
child: TextField(
controller: _searchTextCtrl,
onSubmitted: (value) {
actionSearch();
},
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Please input 1 or 2',
),
),
),
Flexible(
child: _stage == Stage.DONE
? PlaylistTree(_playlistList)
: _stage == Stage.ERROR
? Center(child: Text("$_errorMessage"))
: Center(
child: CircularProgressIndicator(),
),
)
],
),
),
);
}
}
class PlaylistTree extends StatelessWidget {
PlaylistTree(this.playlistList);
final List playlistList;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: playlistList.length,
itemBuilder: (context, index) {
var data = playlistList[index];
return Container(
child: Text("${data['id']} - ${data['first_name']}"),
);
},
);
}
}
Last Step
make provider to handle Business Logic
videosProvider.dart
enum Stage { ERROR, LOADING, DONE }
class VideosProvider with ChangeNotifier {
String errorMessage = "Network Error";
Stage stage;
List _playlist;
int _currentVideoId;
VideosProvider() {
this.stage = Stage.LOADING;
initScreen();
}
void initScreen() async {
try {
await fetchVideosList();
stage = Stage.DONE;
} catch (e) {
stage = Stage.ERROR;
}
notifyListeners();
}
List get playlist => _playlist;
void setPlayList(videosList) {
_playlist = videosList;
}
void validateInput(String valueText) {
if (valueText == ""){
this._currentVideoId = null;
return;
}
try {
int valueInt = int.parse(valueText);
if (valueInt == 1 || valueInt == 2){
this._currentVideoId = valueInt;
}
else {
this.errorMessage = "Use only 1 and 2";
throw 1;
}
} on FormatException catch (e) {
this.errorMessage = "Must be a number";
throw 1;
}
}
void updateCurrentVideoId(String value) async {
this.stage = Stage.LOADING;
notifyListeners();
try {
validateInput(value);
await fetchVideosList();
stage = Stage.DONE;
} on SocketException catch (e) {
this.errorMessage = "Network Error";
stage = Stage.ERROR;
} catch (e) {
stage = Stage.ERROR;
}
notifyListeners();
}
Future fetchVideosList() async {
String url;
if (_currentVideoId != null) {
url = "https://reqres.in/api/users?page=$_currentVideoId";
} else {
url = "https://reqres.in/api/users";
}
http.Response response = await http.get(url);
var videosList = json.decode(response.body)["data"];
setPlayList(videosList);
}
}
Old answer : Aug 19 2019
In my case :
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 as a class, need to update their latest value from API. So, initially, it will request to some URL and updates corresponding values.
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
}
In your case :
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
}
}
When using Provider for state management you don't need to use StatefullWidget, so how can you call a method of the ChangeNotifier on start of the app?
You can simply do that in the constructor of the ChangeNotifier, so that when you point out VideosProvider() to the ChangeNotifierProvider Builder the constructor will get called the first time the provider constructs the VideosProvider, so:
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;
}
}
When using a Provider you don’t need to use a StatefulWidget (as of a tutorial by the Flutter team State management
You may use the following tutorial to see how to fetch data with a provider and a
StatelessWidget: Flutter StateManagement with Provider

navigation transition hangs during Future

i have the below code , that when the button is pressed on Page1() , the future is executed when Page2() loads, but the CircularProgressIndicator() "freezes", until the future completes. I have tried this with BottomNavigationBar as well, and the "slide-in" freezes half way there as well.
is there a more idiomatic way to do this so that Page2() renders fully while the future is running ?
//-----------------------
//main.dart
//-----------------------
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return new MaterialApp(
routes: {
'/' : (BuildContext context) => Page1(),
'/page2' : (BuildContext context) => Page2()
}
);
}
}
//-----------------------
//page1.dart
//-----------------------
class Page1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Page 1')),
body: Container(
child: Column(children: <Widget>[
Text('Page 1 header'),
RaisedButton(
child: Text('Click me'),
onPressed: () {
Navigator.of(context).pushReplacementNamed('/page2');
})
],),)
);
}
}
//-----------------------
//page2.dart
//-----------------------
class Page2 extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _Page2State();
}
}
class _Page2State extends State<Page2> {
MainModel model = MainModel();
void initState() {
model.fetchData();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Page 2')),
body: ScopedModel<MainModel>(
model: model,
child: ScopedModelDescendant(
builder: (context, child, MainModel model) {
if (model.isLoading) {
return Center(child: CircularProgressIndicator());
} else {
return Container(
child: Column(
children: <Widget>[
Text('Page 2 body'),
],
));
}
})));
}
}
//-----------------------
//main_model.dart
//-----------------------
class MainModel extends Model {
bool _isLoading = false;
bool get isLoading => _isLoading;
void fetchData() async {
_isLoading = true;
notifyListeners();
final String url = 'https://jsonplaceholder.typicode.com/todos/1';
await http.get(url)
.then<Null>((http.Response response) {
print('${DateTime.now()} In http response and about to sleep');
sleep(const Duration(seconds:5));
print('${DateTime.now()} done sleeping');
_isLoading = false;
notifyListeners();
return;
}
).catchError((error) {
print('Error: $error');
_isLoading = false;
notifyListeners();
return;
});
}
}
The problem is that the sleep() method freezes the ui.
You could try a Future.delayed() in the following way:
class MainModel extends Model {
bool _isLoading = false;
bool get isLoading => _isLoading;
void fetchData() {
_isLoading = true;
notifyListeners();
final String url = 'https://jsonplaceholder.typicode.com/todos/1';
http.get(url).then<Null>((http.Response response) {
print('${DateTime.now()} In http response and about to sleep');
Future.delayed(Duration(seconds: 5), () {
_isLoading = false;
notifyListeners();
print('${DateTime.now()} done sleeping');
});
}).catchError((error) {
print('Error: $error');
_isLoading = false;
notifyListeners();
return;
});
}
}
Flutter recommends using the FutureBuilder widget when working with async data sources. For example:
FutureBuilder<Post>(
future: fetchPost(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner
return CircularProgressIndicator();
},
);
This way the CircularProgressIndicator will keep running whilst your Page2 loads the API data.