Flutter reload Ui after refreshing FutureBuilder - flutter

in this my code, when application couldn't get data from web server, that can be show reload button, after that when i click on reload button, my method can be get data from web server again, my problem is after this action i can't reload UI with this data and reload button is shown always
class _LessonDetailState extends BaseState<LessonDetail> {
String monthKey;
String lessonFileKey;
int monthId;
_LessonDetailState(this.monthKey, this.lessonFileKey,this.monthId);
Future<PlayLessonResponse> _myResponse;
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = new GlobalKey<RefreshIndicatorState>();
#override
void initState() {
Future.delayed(Duration.zero,() {
_myResponse = _getLessonDetail(context, monthKey, lessonFileKey);
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
body: FutureBuilder<PlayLessonResponse>(
future: _myResponse,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if(snapshot.hasData){
//...
}else{
return RefreshIndicator(
key: _refreshIndicatorKey,
child: Container(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.all(15),
child: Text(
'Error',
style: AppTheme.of(context).caption(),
),
),
RaisedButton(
color: Colors.white,
child: Text(
'Reload',
style: AppTheme.of(context).caption(),
),
onPressed: (){
return _getLessonDetail(context, monthKey, lessonFileKey);
},
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0))),
],
),
),
onRefresh: (){
return _getLessonDetail(context, monthKey, lessonFileKey);
},
);
}
}
return Center(
child: CircularProgressIndicator(),
);
}),
),
);
}
Future<PlayLessonResponse> _getLessonDetail(BuildContext context, String monthKey, String lessonFileKey) async {
try{
//...
return PlayLessonResponse.fromJson(response.body);
}catch(error){
print(error);
return null;
}
}
}

In your onRefresh's _getLessonDetail() function, you should call setState() to rebuild the widget
setState(() {
_myResponse = _getLessonDetail(context, monthKey, lessonFileKey);
});

Related

How to access methods in one class an call it in another classes method in flutter (dart)

I have a method called handleSignIn. I want to call it inside a class that handles sign in when the screen orientantion is mobile. How can I access the method from one class to another class?
this is my first class
class _SignInState extends State<SignIn> {
#override
void initState() {
super.initState();
MsalMobile.create('assets/auth_config.json', authority).then((client) {
setState(() {
msal = client;
});
refreshSignedInStatus();
});
}
/// Signs a user in
void handleSignIn() async {
await msal.signIn(null, [SCOPE]).then((result) {
// ignore: unnecessary_statements
refreshSignedInStatus();
}).catchError((exception) {
if (exception is MsalMobileException) {
logMsalMobileError(exception);
} else {
final ex = exception as Exception;
print('exception occurred');
print(ex.toString());
}
});
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
backgroundColor: Color(0xff392850),
body: Responsive(
mobile: _HomeScreenMobile(
),
// desktop: _HomeScreenDesktop(),
),
),
);
}
}
my _HomeScreenMobile class
class _HomeScreenMobile extends StatelessWidget{
bool isSignedIn = false;
Widget build(BuildContext context) {
ProgressDialog progressDialog = ProgressDialog(context, type:ProgressDialogType.Normal, isDismissible: false, );
progressDialog.style(message: "Signing you in ...");
return Scaffold(
body: Builder(
builder: (context) => Stack(
fit: StackFit.expand,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image.asset('assets/landing.webp',
fit: BoxFit.fill,
color: Color.fromRGBO(255, 255, 255, 0.6),
colorBlendMode: BlendMode.modulate),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: 10.0),
Container(
width: 130.0,
child: Align(
alignment: Alignment.center,
child: RaisedButton(
shape: RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0)),
color: Color(0xffffffff),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Icon(
FontAwesomeIcons.microsoft,
color: Color(0xFF01A6F0),
),
// Visibility(
// visible: !isSignedIn,
SizedBox(width: 10.0),
Visibility(
visible: !isSignedIn,
child: Text(
'Sign in',
style: TextStyle(
color: Colors.black, fontSize: 18.0),
),
),
],
),
onPressed: () => {
progressDialog.show(),
handleSignIn(),
})),
)
],
),
],
),
),
);
}
}
how can I access handleSign from _HomeScreenMobile without it throwing the error The method 'handleSignIn' isn't defined for the type '_HomeScreenMobile'.. Have tried going through the example shared no luck
HomeScreenMobile could get its reference as a parameter and call it whenever it's necessary.
class _HomeScreenMobile extends StatelessWidget{
bool isSignedIn = false;
_HomeScreenMobile({this.handleSignInReference});
final Future<void> Function() handleSignInReference;
...
onPressed: () => {
progressDialog.show(),
handleSignInReference(),
}
}
Finally, where you call this class:
Responsive(
mobile: _HomeScreenMobile(
handleSignInReference:handleSignIn
),
)
You could create a handle_signin.dart file:
void handleSignIn() async {
await msal.signIn(null, [SCOPE]).then((result) {
refreshSignedInStatus();
}).catchError((exception) {
if (exception is MsalMobileException) {
logMsalMobileError(exception);
} else {
final ex = exception as Exception;
print('exception occurred');
print(ex.toString());
}
});
}
Import it wherever you need it:
import './handle_signin.dart`;
And use it:
#override
Widget build() {
return Scaffold(body: Center(GestureDetector(onTap: () async { await handleSignIn(); })));
}
Important note: while the code above might work for your case, it's highly recommended that you consider more sophisticated approaches to state management and Widget communication, such as BLoC.

Flutter - CircularProgressIndicator won't stop executing

My problem is that the CircularProgressIndicator won't stop buffering. My goal is when the video detects a lagging status then the CircularProgressIndicator will show and when it plays again then the CircularProgressIndicator will stop showing.
bool vidBuffering = true;
void buffer() {
setState(() {
vidBuffering = !vidBuffering;
});
}
This is inside a widget where I call the vidBuffering
Widget _buildPlayStack() {
return Stack(
children: [
_buildPlay(),
child: FlatButton(
onPressed: () => setState(() {
_vidController.value.isPlaying ? _vidController.pause() :_vidController.play();
}),
child: Center(
child: (_vidController.value.isPlaying)
? Icon(Icons.pause, color: Colors.green)
: Icon(Icons.play_arrow, color: Colors.green),
),
),
Center(
child: _vidBuffering
? const CircularProgressIndicator()
: null),
],
);
}
Widget _buildPlay() {
return Container(
child: AspectRatio(
aspectRatio: _vidController.value.aspectRatio,
child: VideoPlayer(_controller),
),
);
}
You should be used StreamBuilder for update only Loader
For Example :
class Demo extends StatefulWidget {
#override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
bool vidBuffering = true;
StreamController streamControllerBuffering = StreamController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildPlayStack(),
RaisedButton(
onPressed: () => buffer(),
child: Text(vidBuffering ? 'Hide' : 'Show'),
)
],
),
),
);
}
void buffer() {
vidBuffering = !vidBuffering;
streamControllerBuffering.sink.add(vidBuffering);
}
Widget _buildPlayStack() {
return Stack(
children: [
_buildPlay(),
FlatButton(
onPressed: () => setState(() {
/*_vidController.value.isPlaying
? _vidController.pause()
: _vidController.play();*/
}),
child: Center(
child: (/*_vidController.value.isPlaying*/ true)
? Icon(Icons.pause, color: Colors.green)
: Icon(Icons.play_arrow, color: Colors.green),
),
),
Center(
child: StreamBuilder(
initialData: true,
stream: streamControllerBuffering.stream,
builder: (builder, snapshot) {
if (snapshot.hasData && snapshot.data) {
return CircularProgressIndicator();
} else {
Offstage();
}
return Offstage();
}),
),
],
);
}
Widget _buildPlay() {
return Container(
height: 200,
width: 200,
color: Colors.amber,
);
}
#override
void dispose() {
streamControllerBuffering.close();
super.dispose();
}
}

Streambuilder render twice, the first time with initial data the second one with correct data, but the widget doesn't update

Hi I'm new with flutter and I have troubles with the state. I'm using Bloc to handle state, but when I try to set a state in my first view I can't show it in my second view.
In the second view, streambuilder shows initial value first and connection waiting, then get correct data and state change to active, but the widget doesn't update.
First View
class LoginScreen extends StatefulWidget {
LoginScreen({Key key, this.title}) : super(key: key);
final String title;
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
Widget _divider() {
return Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Row(
children: <Widget>[
SizedBox(
width: 30,
),
Expanded(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 30),
child: Divider(
thickness: 1,
),
),
),
SizedBox(
width: 20,
),
],
),
);
}
final _emailController = TextEditingController();
final _passwordController =TextEditingController();
_updateEmail (String text) => userBloc.updateUser(text);
Widget _emailPasswordWidget() {
return Column(
children: <Widget>[
EntryFields("Ingresa tu email", value:_emailController, onchanged: _updateEmail),
EntryFields("Ingresa tu Password",value: _passwordController, isPassword: true),
],
);
}
_goSignupScreen() async { return Navigator.pushNamed(context, Routes.signupRoute ); }
_goHomeScreen() async {
_updateEmail(_emailController.text);
return Navigator.pushNamed(context, Routes.homeRoute );
}
#override
Widget build(BuildContext context) {
final height = MediaQuery.of(context).size.height;
return Scaffold(
body: Container(
height: height,
child: Stack(
children: <Widget>[
Positioned(
top: -height * .15,
right: -MediaQuery.of(context).size.width * .4,
child: BezierContainer()),
Container(
padding: EdgeInsets.symmetric(horizontal: 20),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
SizedBox(height: height* 0.2),
Center(child: AppIconWidget(image: 'assets/images/logo.png', scale: 0.1)),
SizedBox(height: 50),
_emailPasswordWidget(),
SizedBox(height: 20),
SubmitButton(title: 'Iniciar Sesión', onPressed: _goHomeScreen ),
Container(
padding: EdgeInsets.symmetric(vertical: 10),
alignment: Alignment.centerRight,
child: Text('Olvidaste tu contraseña?',
style: TextStyle(
fontSize: 14, fontWeight: FontWeight.w500)),
),
_divider(),
FacebookSignInButton(onPressed: (){}),
GoogleSignInButton(onPressed: () {}),
SizedBox(height: height * .055),
AccountLabel(label: 'No tienes cuenta?', btnText: 'Registrate', onPressed: _goSignupScreen ),
],
),
),
),
// Positioned(top: 40, left: 0, child: BackBtn() ),
],
),
),);
}
}
Bloc
class UserBloc {
final _userRepository = UserRepository();
final _userController = PublishSubject();;
Observable get getUser => _userController.stream;
updateUser(user) async {
var currentUser = await _userRepository.updateCurrentUser(user);
_userController.sink.add(currentUser);
return null;
}
dispose() {
_userController.close();
}
}
final userBloc = UserBloc();
Provider
class UserProvider {
Future updateCurrentUser(user) async{
return currentUser = user;
}
}
Repository
class UserRepository {
final userProvider = UserProvider();
Future updateCurrentUser(user) {
return userProvider.updateCurrentUser(user);
}
}
Second View
class HomeScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('title')),
body: StreamBuilder(
initialData: 'here!',
stream: userBloc.getUser,
builder: (BuildContext context, AsyncSnapshot snapshot) {
print('...');
print('${snapshot.data}');
print('${snapshot.connectionState}');
print('...');
if (snapshot.hasError) return Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('Select lot');
case ConnectionState.waiting:
return Text('Awaiting bids...');
case ConnectionState.active:
return Text('\$${snapshot.data}');
case ConnectionState.done:
return Text('\$${snapshot.data} (closed)');
}
return null; // unreachable
},
),
);
}
Thanks!
instead of doing switch for connectionState, i think it is better if you use snapshot.hasData
if (snapshot.hasData) {
return Text('\$${snapshot.data}');
} else {
return Text('Loading;);
}

Flutter - Returning to previous page from AppBar is not refreshing the page, with Navigator.pop(context)

I was trying to get the list page refreshed if a method was run on another page. I do pass the context using the push navigation.
I tried to follow these 3 answers Answer 1 Answer 2 and Answer 3 and I am not able to manage the states here.
This is the first list page which needs to be refreshed. It calls a class
class _PageLocalState extends State<PageLocal> {
#override
Widget build(BuildContext context) {
return Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: SafeArea(
child: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: widget.allLocal.length,
//padding: const EdgeInsets.only(top: 10.0),
itemBuilder: (context, index) {
return LocalCard(widget.allLocal[index]);
},
)),
)
],
),
);
}
}
The next class:
class LocalCardState extends State<LocalCard> {
FavData localdet;
LocalCardState(this.localdet);
ListTile makeListTile() => ListTile(
contentPadding: EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
title: Text(
localdet.name,
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(localdet.loc),
trailing: Icon(Icons.keyboard_arrow_right, size: 30.0),
onTap: () => navigateToDetail(localdet),
);
Widget get localCard {
return new Card(
elevation: 4.0,
margin: new EdgeInsets.symmetric(horizontal: 10.0, vertical: 6.0),
child: Container(
child: makeListTile(),
));
}
#override
Widget build(BuildContext context) {
return new Container(
child: localCard,
);
}
navigateToDetail(FavData localdet) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FavouriteDetailPage(
mndet: localdet,
)));
setState(() {});
}
}
Now this is routing to the final detail page:
class _FavouriteDetailPageState extends State<FavouriteDetailPage> {
bool isFav = false;
FavData mndet;
_FavouriteDetailPageState(this.mndet);
// reference to our single class that manages the database
final dbHelper = DatabaseHelper.instance;
#override
Widget build(BuildContext context) {
Widget heading = new Container(...);
Widget middleSection = new Expanded(...);
Widget bottomBanner = new Container(...);
Widget body = new Column(...);
final makeBottom = Container(
height: 55.0,
child: BottomAppBar(
color: Color.fromRGBO(36, 36, 36, 1.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FavIconWidget(mndet),
],
),
),
);
return Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text('The Details'),
backgroundColor: Color.fromRGBO(36, 36, 36, 1.0),
),
body: Container(
child: Card(
elevation: 5.0,
shape: RoundedRectangleBorder(
side: BorderSide(color: Colors.white70, width: 1),
borderRadius: BorderRadius.circular(10),
),
margin: EdgeInsets.all(20.0),
child: Padding(
padding: new EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: body,
),
),
),
bottomNavigationBar: makeBottom,
);
}
void share(BuildContext context, FavData mndet) {
final RenderBox box = context.findRenderObject();
final String shareText = "${mndet.name} - ${mndet.desc}";
Share.share(shareText,
subject: mndet.loc,
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size);
}
}
class FavIconWidget extends StatefulWidget {
final FavData mnforIcon;
FavIconWidget(this.mnforIcon);
#override
_FavIconWidgetState createState() => _FavIconWidgetState();
}
class _FavIconWidgetState extends State<FavIconWidget> {
final dbHelper = DatabaseHelper.instance;
Future<bool> get isFav async {
final rowsPresent = await dbHelper.queryForFav(widget.mnforIcon.id);
if (rowsPresent > 0) {
print('Card Loaded - Its Favourite already');
return false;
} else {
print('Card Loaded - It is not favourite yet');
return true;
}
}
void _insert() async {...}
void _delete() async {...}
#override
Widget build(BuildContext context) {
return FutureBuilder<bool>(
future: isFav,
initialData:
false, // you can define an initial value while the db returns the real value
builder: (context, snapshot) {
if (snapshot.hasError)
return const Icon(Icons.error,
color: Colors.red); //just in case the db return an error
if (snapshot.hasData)
return IconButton(
icon: snapshot.data
? const Icon(Icons.favorite_border, color: Colors.white)
: Icon(Icons.favorite, color: Colors.red),
onPressed: () => setState(() {
if (!snapshot.data) {
print('Its favourite so deleting it.');
_delete();
} else {
print('Wasnt fav in the first place so inserting.');
_insert();
}
}));
return CircularProgressIndicator(); //if there is no initial value and the future is not yet complete
});
}
}
I am sure this is just some silly coding I have done but just not able to find out. Where.
I tried adding Navigator.pop(context); in different sections of the detail page and it fails.
Currently, I have to navigate back to the Favourites list page and then HomePage and then back to Favourites ListPage to refresh the list.
try this.. Anywhere you are using Navigator.pop or Navigator.push .. Instead of this use this:
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) => Password())
);
//instead of Password use the name of the page(the second page you want to go to)

Showing selected image in alert dialog in flutter

How can i show the selected image in my alert dialog ?
In my app, i added an alert dialog which has the camera button. When user clicks the camera button, another alert dialog asks to select file from gallery. After the user selects image file from gallery, i want to show the image in the alert dialog with the camera button, but the image shows only after reopening the alert dialog.
I have posted my code below. I am new to flutter. Please can someone help me? Thanks in advance.
class Test extends StatefulWidget {
#override
_State createState() => new _State();
}
Future<File> imageFile;
class _State extends State<Test> {
Future<void> _openDailog() async {
return showDialog<void>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Click Photo'),
Ink(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24.0),
color: Colors.blue),
child: IconButton(
color: Colors.white,
icon: Icon(Icons.camera_alt),
onPressed: () {
_cameraOptions();
print("test");
},
),
)
],
),
content: SingleChildScrollView(
child: Container(
width: 300.0,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
showImage(),
InkWell(
child: Container(
margin: EdgeInsets.only(top: 8.0),
child: RaisedButton(
color: Colors.blue,
child: new Text(
"Send",
style: TextStyle(color: Colors.white),
),
onPressed: () {
Navigator.of(context).pop();
print("test");
},
),
)),
],
),
),
),
);
},
);
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FloatingActionButton(
heroTag: null,
child: Icon(Icons.insert_drive_file),
onPressed: () {
_openDailog();
},
)
],
);
}
Future<void> _cameraOptions() {
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: new SingleChildScrollView(
child: new ListBody(
children: <Widget>[
FlatButton(
onPressed: () {
pickImageFromGallery(ImageSource.gallery);
Navigator.of(context).pop();
},
color: Colors.transparent,
child: new Text(
'Select From Gallery',
textAlign: TextAlign.start,
style: new TextStyle(
decoration: TextDecoration.underline,
),
),
),
],
),
),
);
});
}
pickImageFromGallery(ImageSource source) {
setState(() {
imageFile = ImagePicker.pickImage(source: source);
});
}
Widget showImage() {
return FutureBuilder<File>(
future: imageFile,
builder: (BuildContext context, AsyncSnapshot<File> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
return Image.file(
snapshot.data,
width: MediaQuery.of(context).size.width,
height: 100,
);
} else if (snapshot.error != null) {
return const Text(
'Error Picking Image',
textAlign: TextAlign.center,
);
} else {
return const Text(
'No Image Selected',
textAlign: TextAlign.center,
);
}
},
);
}
}
That is because you would need to setState() however you can't do that in an alert dialogue as it doesn't have its own state, the workaround for that would be to have the dialogue be its own stateful widget. Please check out this article as it shows how to do that. If you faced problems let me know!
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
void main() {
runApp(new MaterialApp(
home: new MyHomePage(),
));
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _selectedIndex = 0;
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("StackoverFlow"),
),
body: Container(),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await _dialogCall(context);
},
),
);
}
Future<void> _dialogCall(BuildContext context) {
return showDialog(
context: context,
builder: (BuildContext context) {
return MyDialog();
});
}
}
class MyDialog extends StatefulWidget {
#override
_MyDialogState createState() => new _MyDialogState();
}
class _MyDialogState extends State<MyDialog> {
String imagePath;
Image image;
#override
Widget build(BuildContext context) {
return AlertDialog(
content: new SingleChildScrollView(
child: new ListBody(
children: <Widget>[
Container(child: image!= null? image:null),
GestureDetector(
child: Row(
children: <Widget>[
Icon(Icons.camera),
SizedBox(width: 5),
Text('Take a picture '),
],
),
onTap: () async {
await getImageFromCamera();
setState(() {
});
}),
Padding(
padding: EdgeInsets.all(8.0),
),
],
),
),
);
}
Future getImageFromCamera() async {
var x = await ImagePicker.pickImage(source: ImageSource.camera);
imagePath = x.path;
image = Image(image: FileImage(x));
}
}
Try this solution with GestureDetector() .it works
onTap:()async{
var image = await ImagePicker.pickImage(
source: ImageSource.gallery).whenComplete((){
setState(() {
});
}
);
setState(() {
_image = image;
});
},