How to refetch the data inside a FutureBuilder in Flutter? - flutter

Here is my code:
#override
void initState() {
super.initState();
weatherFuture = _getWeather();
}
#override
Widget build(BuildContext context) {
double viewBoxWidth = MediaQuery.of(context).size.width;
return Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FutureBuilder(
future: weatherFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Container(
width: viewBoxWidth * 0.35,
color: Colors.orange, // Todo: Remove this color later
child: WeatherTileColumn(
topTileDisplayText: snapshot.data.areaName,
bottomTileDisplayText: snapshot.data.weatherDescription,
topTileIcon: '🗺',
bottomTileIcon:
_getWeatherIcon(snapshot.data.weatherConditionCode),
),
);
} else {
return Text(
'Loading Data...',
style: TextStyle(fontFamily: 'Merriweather', fontSize: 15),
textAlign: TextAlign.center,
);
}
}),
Container(
width: viewBoxWidth * 0.2,
margin: EdgeInsets.only(top: 95),
child: TextButton(
child: Icon(
Icons.refresh,
color: Colors.black87,
size: 55,
),
style: ButtonStyle(
backgroundColor:
MaterialStateProperty.all(Colors.transparent)),
onPressed: () {
setState(() {
weatherFuture = _getWeather();
});
},
),
),
FutureBuilder(
future: weatherFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Container(
width: viewBoxWidth * 0.35,
color: Colors.purple,
child: WeatherTileColumn(
topTileDisplayText: snapshot.data.temperature.toString(),
bottomTileDisplayText:
snapshot.data.tempFeelsLike.toString(),
topTileIcon: '🌡',
bottomTileIcon: '😶‍🌫️',
),
);
} else {
return Text(
'Loading Data...',
style: TextStyle(fontFamily: 'Merriweather', fontSize: 15),
textAlign: TextAlign.center,
);
}
},
)
],
),
);
In this code, if you look at the main widgets inside the children of the Row widget, you will see that there are two FutureBuilder widgets and a Container widget.
What has to happen is upon pressing that TextButton inside the Container, I want the two future builder widgets to be re updated with newly fetched data.
My current attempt was to do:
setState() {
weatherFuture = _getWeather();
}
inside the callback function for the onPressed method. And it does not work. I looked at many solutions and articles and could not get this to work.
(Also I checked Reload data when using FutureBuilder stack overflow post and this is not a duplicate. In that post, the button that rebuilds the FutureBuilder widget is inside the FutureBuilder widget but in this case it isn't. So that solution did not work for me.)

Related

Type 'Future<dynamic>' is not subtype of type 'Widget'

I am showing markers from API on google maps. Here is my build method. When the program reaches the _widgetbuilder() method, it throws the specific error of type Future is not a subtype of the widget. If someone could please help to solve the problem and also tell me that what exactly this error means.....
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: FutureBuilder<List<MarkersOnMap>>(
future: future,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData)
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
if (snapshot.hasData && snapshot.data.isEmpty) {
return Center(
child: Container(
child: Column(
children: [
Text(
'No Properties Added Yet\nPlease Add Some!',
style:
TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
ElevatedButton.icon(
onPressed: () {
Navigator.push(
context,
PageTransition(
duration: Duration(microseconds: 500),
type: PageTransitionType.fade,
child: AddNewEproperty(
createEproperty: widget.createEproperty),
),
);
},
label: Text('Add'),
icon: Icon(Icons.add),
),
],
),
),
);
} else
_widgetbuilder();
if (snapshot.hasData) {
return ListView.builder(
itemCount: allWidgets.length + 1,
shrinkWrap: true,
padding: EdgeInsets.only(top: 16),
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, i) {
return Stack(
children: <Widget>[
Container(),],);},);},},),);}
This is the _widgetbuilder() method. When it reaches this return _widgetbuilder, throws _typeerror.
_widgetbuilder() async {
allWidgets = [];
widget.markersonmap = await future;
widget.markersonmap.forEach(
(element) {
print(element);
allWidgets.add(
Container(
height: 25,
width: 50,
child: new DecoratedBox(
decoration: BoxDecoration(
border: Border.all(color: Colors.black),
borderRadius: BorderRadius.circular(5.0),
color: Colors.black54),
child: Text(
element.ePropertiesCardsList.price.toString(),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white),
),
),
),
);
},
);
}
You are getting this error because your function _widgetbuilder returns Future<dynamic> because the function is async.
Widget _widgetbuilder(){
// code here
}
The function should be in this structure to return of type Widget. The code that needs to be async should either be taken out of build function or use .then pattern for async code instead of async await if you really need to.
This short 9 min video will help you understand async better in flutter.
In here now the type error is solved but after reading 'future.then..... it does not goto the future and fetch data but jumps to the next foreach line and then calls it as null.
_widgetbuilder() {
allWidgets = [];
// widget.markersonmap = await future;
future.then((value) {
widget.markersonmap = value;
});
widget.markersonmap.forEach(
(element) {
print(element);
allWidgets.add(
Container(
// other code
}

how to make flutter searchDelagate a separate screen that can be navigated to independently?

i have a page called searchUsersSCreen which is this:
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:myApp/models/otherUser.dart';
import 'package:myApp/ui/widgets/user_profile.dart';
import 'database.dart';
class SearchUsersScreen extends StatefulWidget {
#override
_SearchUsersScreenState createState() => _SearchUsersScreenState();
}
class _SearchUsersScreenState extends State<SearchUsersScreen> {
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => showSearch(
context: context,
delegate: SearchUsers(
DatabaseService().fetchUsersInSearch(),
),
));
}
#override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).primaryColor,
);
}
}
and inside the same dart file is have this searchDelegate :
//Search delegate
class SearchUsers extends SearchDelegate<OtherUser> {
final Stream<QuerySnapshot> otherUser;
final String hashtagSymbol = 'assets/svgs/flaticon/hashtag_symbol.svg';
SearchUsers(this.otherUser);
#override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = '';
},
),
];
}
#override
Widget buildLeading(BuildContext context) {
return Container(
width: 0,
height: 0,
);
}
#override
Widget buildResults(BuildContext context) {
return Container(
width: 0,
height: 0,
color: Theme.of(context).primaryColor,
);
}
#override
Widget buildSuggestions(BuildContext context) {
showUserProfile(String userId) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => UserProfileView(
userUid: userId,
)));
}
return StreamBuilder<QuerySnapshot>(
stream: DatabaseService().fetchUsersInSearch(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
final handlesResults = snapshot.data.documents
.where((u) => u['username'].contains(query));
if (!snapshot.hasData) {
return Container(
color: Theme.of(context).primaryColor,
child: Center(
child: Text(
'',
style: TextStyle(
fontSize: 16, color: Theme.of(context).primaryColor),
),
),
);
}
if (handlesResults.length > 0) {
return Container(
color: Theme.of(context).primaryColor,
child: ListView(
children: handlesResults
.map<Widget>((u) => GestureDetector(
child: Padding(
padding: const EdgeInsets.all(0.1),
child: Container(
padding: EdgeInsets.symmetric(vertical: 5),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border(
bottom: BorderSide(
width: 0.3, color: Colors.grey[50]))),
child: ListTile(
leading: CircleAvatar(
backgroundColor:
Theme.of(context).primaryColor,
backgroundImage:
NetworkImage(u['userAvatarUrl']),
radius: 20,
),
title: Container(
padding: EdgeInsets.only(left: 10),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(u['username'],
style: TextStyle(
fontSize: 16,
color: Theme.of(context)
.accentColor),
overflow: TextOverflow.ellipsis),
SizedBox(
height: 5,
),
Text(u['name'],
style: TextStyle(
fontSize: 16,
color: Colors.grey[500],
),
overflow: TextOverflow.ellipsis),
],
),
),
trailing: Container(
padding: EdgeInsets.only(left: 10),
height: 43.0,
width: MediaQuery.of(context).size.width / 2,
),
),
),
),
onTap: () {
showUserProfile(u['id']);
},
))
.toList(),
),
);
} else {
return Container(
color: Theme.of(context).primaryColor,
child: Center(
child: Text(
'No results found',
style: TextStyle(
fontSize: 16,
color: Theme.of(context).accentColor,
),
),
),
);
}
});
}
}
i wanted to user the class SearchUsers as a separate screen that i can navigate to independently...but couldn't achieve that as SearchUsers doesn't evaluate to a widget.
so i built SearchUsersScreen statefulWidget and inside it's initState() i called this:
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => showSearch(
context: context,
delegate: SearchUsers(
DatabaseService().fetchUsersInSearch(),
),
));
}
as to make the search feature starts automatically when the user navigates to SearchUsersScreen.
and i ended up into two problems:
SearchUsers is being displayed in full screen ontop of SearchUsersSCreen (which i don't want this behavior), i want it to be displayed inside of it.
actually its covering the BottomNavigationBar i built for navigation between screens.
after SearchUsers is being displayed (and its doing its job well), when i tap the device back button...i leave SearchUsers and get back to SearchUsersScreen....which is indeed a blank screen.
so to wrap it up...all i want is to use SearchUsers class as a widget that i can navigate to and navigate from independently...thats it.
any help would be much appreciated.
thanks for your time reading.
Instead of trying to create a separate widget SearchUsers, try to create a dialog and show it when anyone wants to search users. You can also use the navigator and the back button in this case and get arguments passed from the next screen to the previous screen.

How to call navigator inside Stream Builder in flutter?

I have a problem that when I try to put the the navigator pop() method inside the string builder this error appears: setState() or markNeedsBuild() called during build.
I read that you can't call the navigator inside the stream builder so does anyone have any ideia on how to fix this?
This is my code:
return Scaffold(
resizeToAvoidBottomPadding: false,
body: Center(
child: Container(
padding: EdgeInsets.all(15),
color: Colors.white,
child: Column(
children: <Widget>[
SizedBox(height: 55),
SvgPicture.asset('images/svg_example.svg'),
SizedBox(height: 55),
Text("Login App",
style: TextStyle(
fontWeight: FontWeight.bold, color: Colors.black)),
SizedBox(height: 40),
emailField,
SizedBox(height: 45),
passwordField,
SizedBox(height: 45),
loginButton,
SizedBox(height: 15),
StreamBuilder<ApiResponse<LoginResponse>>(
stream: userBloc.authenticationUserStream,
builder: (context,
AsyncSnapshot<ApiResponse<LoginResponse>> snapshot) {
// it will observe changes on the ApiResponse<LoginResponse>
if (!snapshot.hasData) return Container();
switch (snapshot.data.status) {
case Status.LOADING:
return Loading(
loadingMessage: "loading",
);
case Status.COMPLETED:
prefs.saveTokenPref(snapshot.data.data.token);
prefs.saveUserPref(snapshot.data.data.user);
goToMain();
return Container(width: 0.0, height: 0.0);
case Status.ERROR:
// Here you can go to another screen after login success.
return Center(
child: Text("${snapshot.data.message}"),
);
default:
return Container();
}
},
)
],
)),
),
);
}
goToMain() {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MainScreen()),
);
}
Ok, so given my question has a -1 because someone just feel like giving without really helping the post I put here the answer for this question:
You just need to do this on your initState()
This will listen to the stream itself and make UI logic outside the Stream Builder.
#override
void initState() {
userBloc = UserBloc();
super.initState();
userBloc.userSubject.listen((state) {
if (state.status == Status.COMPLETED) {
goToMain();
}
});
}

How to access Provider providers in Dialogs in Flutter

The Provider package makes use of InheritedWidget. This is a problem when I want to access a provider when I'm in a Dialog. If I load a dialog using
showDialog(... builder: (context) => MyDialog);
I can't access anything using InheritedWidget because my dialog isn't part of the main widget tree. This also means that I can't access my Provider providers, correct?
My question is: How can I access my providers in a dialog if it's not part of the main app widget tree?
final firebaseAuth = Provider.of<FirebaseAuth>(context);
I have the same problem with using BLoCs. If I try to retrieve them in a dialog via InheritedWidget, they fail. I've gotten around this by passing the BLoC in the constructor but this seems to defeat the purpose of InheritedWidgets.
Instead of passing the BLoC in the constructor, you can make use of BlocProvider.value.
https://pub.dev/documentation/flutter_bloc/latest/flutter_bloc/BlocProvider/BlocProvider.value.html
This will allow you to provide your existing BLoC instance to your new route (the dialog). And you still get all the benefits of InheritedWidget
// Get the BLoC using the provider
MyBloc myBloc = BlocProvider.of<MyBloc>(context);
showDialog(
context: context,
builder: (BuildContext context) {
Widget dialog = SimpleDialog(
children: <Widget>[
... // Now you can call BlocProvider.of<MyBloc>(context); and it will work
],
);
// Provide the existing BLoC instance to the new route (the dialog)
return BlocProvider<MyBloc>.value(
value: myBloc, //
child: dialog,
);
},
);
.value() also exists for ChangeNotifierProvider, ListenableProvider, etc.
https://pub.dev/documentation/provider/latest/provider/ChangeNotifierProvider/ChangeNotifierProvider.value.html
https://pub.dev/documentation/provider/latest/provider/ListenableProvider/ListenableProvider.value.html
I got stuck at this part for a while. I honestly didn't want to pass the provider, also unpacking the widget code to grab the parent context is hard when you are dealing with a complex widget (And it doesn't seem like the best approach).
This made more sense
handleFileViewerClicked(context) async {
var reportState = Provider.of<ReportState>(context, listen: false);
/**
*The dialog will live in a new context and requires a new provider to be created for the report state
* For more information read the Provider.Consumer documentation and showDialog function signature.
*/
showDialog(
context: context,
//Notice the use of ChangeNotifierProvider<ReportState>.value
builder: (_) => ChangeNotifierProvider<ReportState>.value(
value: reportState,
child: FileViewer(),
),
);
}
Your child widget which is FileViewer in that case can make use of
class FileViewer extends StatelessWidget {
.
.
Widget build(BuildContext context) {
//you can enable or disable listen if you logic require so
var reportState = Provider.of<ReportState>(context);
return Text('${reportState.files.length}');
}
}
I was able to access Provider data by passing in the data set into the alert dialog. Interestingly, you have to call setState() in the Dialog in order to see the changes in your Dialog.
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
final provider = Provider.of<DataSet>(context);
return Scaffold(
body: Container(
child: RaisedButton(
child: Text('Show Dialog'),
onPressed: () {
showDialog(context: context,
builder: (context) {
return DialogContent(dataSet: provider);
});
},
),
),
);
}
}
class DialogContent extends StatefulWidget {
final DataSet dataSet;
const DialogContent({Key key, this.dataSet}) : super(key: key);
#override
_DialogContentState createState() => _DialogContentState();
}
class _DialogContentState extends State<DialogContent> {
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text('Dialog with data'),
content: Text('${widget.dataSet.pieceOfData}'),
actions: <Widget>[
FlatButton(
child: Text('Increase Data'),
onPressed: () {
setState(() {
widget.dataSet.increaseData();
});
},
),
],
);
}
}
class DataSet with ChangeNotifier {
int pieceOfData = 1;
increaseData() {
pieceOfData += 1;
notifyListeners();
}
}
Try this. Create a different stateful widget that housed the dialog and return that dialog stateful widget when you call a showDialog() method. Example below
class MainScreen extends StatefulWidget {
#override
_MainScreenState createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build((BuildContext context) {
MainProvider mainProvider = MainProvider.of(context);
return Scaffold(
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.white,
),
body: Center(
child: Container(
child: RaisedButton(
onPressed: ()=> _openBottomSheet(context, mainProvider),
child: Text("Open Dialog"),
)
)
)
);
}
_openBottomSheet(BuildContext context, MainProvider mainProvider) async {
await showModalBottomSheet<bool>(
context: cntxt,
builder: (_) {
return BottomSheetDialog();
}
);
}
}
class BottomSheetDialog extends StatefulWidget {
#override
_BottomSheetDialogState createState() => _BottomSheetDialogState();
}
class _BottomSheetDialogState extends State<BottomSheetDialog> {
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
MainProvider mainProvider = MainProvider.of(context);
return Container(
width: MediaQuery.of(context).size.width,
height:MediaQuery.of(context).size.height/2.2,
margin: EdgeInsets.fromLTRB(16,16,16,0),
decoration: BoxDecoration(
color: mainProvider.color,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20),
topRight: Radius.circular(20),
),
),
child: RaisedButton(
onPressed: ()=> mainProvider.changeColor(),
child: Text("Open Dialog"),
)
)
}
}
class MainProvider with ChangeNotifier {
static MainProvider of(BuildContext context) {
return Provider.of<MainProvider>(context);
}
Color _color = Colors.white;
bool _isColorChanged = false;
Color get color => _color;
bool get isColorChanged => _isColorChanged;
changeColor() {
if(!isColorChanged) {
_color = Colors.green;
}else{
_color = Colors.white;
}
_isColorChanged = !_isColorChanged;
notifyListeners();
}
}
If that's an option for you, simply lift the provider up above MaterialApp. This might be a good solution for globally unique providers, e.g. user configurations or similar:
You have to pass the thing being provided directly to the dialog constructor to access it in the dialog's new context. You can also give it to a new Provider widget at the top of your dialog tree if you have a very deep widget tree in the dialog and you want to access it from somewhere deeper.
If you are using Bloc, typically you tell Provider to call the Bloc's dispose method when the provider widget is disposed to clean up the streamcontrollers/subscriptions. Obviously, you might not want to do this if you are re-providing the bloc to the dialog, or if this bloc is used outside the dialog.
Using stateful or stateless widgets in the dialog is up to you, as long as you have access to the bloc you can use a streambuilder and listen to some stream as per usual.
an example:
class EditEventDialog extends StatelessWidget {
final GroupBloc groupBloc;
EditEventDialog({this.groupBloc})
: assert(groupBloc != null);
#override
Widget build(BuildContext context) {
return Provider(
builder: (context) => groupBloc,
child: Dialog(
child: Container(
height: 400.0,
width: 200.0,
child: StreamBuilder<StatusStreamType>(
stream: groupBloc.statusStream,
builder: (context, snapshot) {
....
and to call it:
onPressed: () => showDialog(
builder: (newContext) {
GroupBloc groupBloc = Provider.of<GroupBloc>(context);
return EditEventDialog(
groupBloc: groupBloc,
);
},
context: context,
)
I faced the same issue today and I was able to work around it by wrapping the dialog in a Stateful Builder and setting the state in the new widget tree.
context: context,
builder: (context) {
return StatefulBuilder(builder: (context, setState) {
return Dialog(
child: SingleChildScrollView(
child: Container(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.symmetric(vertical: height * .05),
child: Text('Choose An Avatar'),
),
Stack(
children: <Widget>[
Align(
alignment: Alignment.center,
child: CircleAvatar(
minRadius: width * .09,
maxRadius: width * .09,
backgroundColor: Colors.brown,
backgroundImage: AssetImage(
'assets/profile${appData.avatar}.png'),
),
),
Positioned.fill(
left: width * .04,
child: Align(
alignment: Alignment.centerLeft,
child: Container(
width: width * .18,
child: Material(
color: Colors.transparent,
child: InkWell(
child: Icon(Icons.arrow_left,
size: width * .18),
onTap: () {
setState(() {
appData.changeAvatar();
});
},
),
),
),
),
),
],
),
],
),
),
),
),
);
});
});
I only way I've found to gain access to the Bloc provider from within the dialog is by defining the dialog outside of the showDialog call.
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocConsumer<MyCubit, MyState>(
listener: (context, state) {
if (state.shouldShowDialog == true) {
final dialog = AlertDialog(
content: Text("Info");
actions: <Widget>[
TextButton(
child: const Text('Approve'),
onPressed: () => {
context
.read<MyCubit>()
.handleDialogApproved();
Navigator.of(context, rootNavigator: true).pop();
}
)
],
);
showDialog<void>(
context: context,
builder: (BuildContext context) {
return dialog;
},
);
}
},
builder: (context, state) {
return Container();
},
);
}
}
Widget reviseRatesButton(BuildContext c) {
return Consumer<RideRequestProvider>(
builder: (c, provider, child) {
return OutlinedButton(
onPressed: () async {
alertDialogNew(
c,
content: ChangeNotifierProvider.value(
value: provider,
builder: (context, child) {
return Consumer<RideRequestProvider>(
builder: (context, provider, child) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
const Text(
"Offer your fare",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
),
),
const SizedBox(
height: 5,
),
CustomTextFormField(
hint: "Enter your fair/day",
keyboardType: TextInputType.number,
controller: provider.fareController,
onChanged: (String? val) {
provider.calculateFare();
},
),
const SizedBox(
height: 5,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Weekly (5 days)',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
Text.rich(
TextSpan(
text: provider.weeklyFare
.toStringAsFixed(2),
children: [
TextSpan(
text: '/week',
style: TextStyle(
color: Colors.blue.shade700,
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
],
),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
),
Column(
children: [
const Text(
'Monthly(22 days)',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
Text.rich(
TextSpan(
text: provider.monthlyFare
.toStringAsFixed(2),
children: [
TextSpan(
text: '/month',
style: TextStyle(
fontSize: 12,
color: Colors.blue.shade700,
fontWeight: FontWeight.w600,
),
),
],
),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
],
),
);
},
);
}),
);
},
child: const Text(
"Revise Rates",
),
style: OutlinedButton.styleFrom(
side: const BorderSide(width: 1.0, color: Colors.blue),
),
);
},
);}
I've been stuck at this for a few moments, but ChangeNotifierProvider.value works like a charm.
A bit late in finding this, but just had this same challenge and realised a solution: You need to maintain a reference to the context outside of the showDialog call. By default we usually just use "context" as the name of the context both outside and inside the showDialog, thus masking the outside context from use within the showDialog. So, instead, use a different name inside the showDialog (e.g. "c") and then you can still use "final firebaseAuth = Provider.of(context);" inside the showDialog and it will find the FirebaseAuth object from the main tree as you wish.
Here's a short excerpt from some code I am working on which works now:
showDialog(
context: context,
builder: (c) {
final action = Provider.of<ActionType>(context);
final host = Provider.of<String>(context);
return AlertDialog(
title: Text('Action API'),
actions: [
FlatButton(
onPressed: () {
Navigator.pop(c);
},
etc.

How to implement drag and drop with flutter

How can I move my container or any other widgets on flutter around the screen and drop at some locations?
I found flutter widgets Draggable and DragTarget. How to use them to implement the drag and drop?
Draggable and DragTarget allow us to drag a widget across the screen.
A Draggable widgets gives the ability to move to any other widget while the DragTarget acts as the sink or drop location for a Draggable widget.
Find the below code sample using which I implemented a simple odd-or-even game
Hell yeah, I'm a Game Developer ◕‿↼
import 'package:flutter/material.dart';
import 'dart:math';
class OddOrEven extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _OddOrEvenState();
}
}
class _OddOrEvenState extends State<OddOrEven> {
bool accepted = false;
Color dotColor = Colors.blue;
GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey();
int val = 0;
int score = 0;
#override
Widget build(BuildContext context) {
// assign a random number to value which will be used as the box value
val = Random().nextInt(100);
return Scaffold(
key: scaffoldKey,
appBar: AppBar(),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
// just a score and mock player name indicator
Padding(
padding: EdgeInsets.all(16.0),
child: Center(
child: Center(
child: Chip(
avatar: CircleAvatar(
backgroundColor: Colors.teal,
child: Text(
score.toString(),
style: TextStyle(color: Colors.white),
),
),
label: Text(
'Player Alpha',
style: TextStyle(
fontSize: 20.0,
color: Colors.black,
fontStyle: FontStyle.italic),
),
),
),
),
),
// here comes our draggable.
// it holds data which is our random number
// the child of the draggable is a container reactangural in shape and
//
Draggable(
data: val,
child: Container(
width: 100.0,
height: 100.0,
child: Center(
child: Text(
val.toString(),
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
color: Colors.pink,
),
// This will be displayed when the widget is being dragged
feedback: Container(
width: 100.0,
height: 100.0,
child: Center(
child: Text(
val.toString(),
style: TextStyle(color: Colors.white, fontSize: 22.0),
),
),
color: Colors.pink,
),
// You can also specify 'childWhenDragging' option to draw
// the original widget changes at the time of drag.
),
// and here this row holds our two DragTargets.
// One for odd numbers and the other for even numbers.
//
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
width: 100.0,
height: 100.0,
color: Colors.green,
// Even holder DragTarget
//
child: DragTarget(
builder: (context, List<int> candidateData, rejectedData) {
print(candidateData);
return Center(
child: Text(
"Even",
style: TextStyle(color: Colors.white, fontSize: 22.0),
));
},
// On will accept gets called just before it accepts the drag source.
// if needed, we can reject the data here. But we are not doing that as this is a GAME !!! :)
onWillAccept: (data) {
print("Will accpt");
return true; //return false to reject it
},
// On accepting the data by the DragTarget we simply check whether the data is odd or even and accept based on that and increment the counter and rebuild the widget tree for a new random number at the source.
onAccept: (data) {
print("On accpt");
if (data % 2 == 0) {
setState(() {
score++;
});
// How did you manage to score 3 points😮
// Congrats. You won the game.
if (score >= 3) {
showDialog(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
title: Text("Congrats!!"),
content: Text("No-brainer...😮"),
actions: <Widget>[
FlatButton(
child: Text("Ok."),
onPressed: () {
Navigator.of(context).pop();
setState(() {
score = 0;
});
},
)
],
);
});
}
} else {
setState(() {});
}
},
),
),
// And here is the Odd-holder
Container(
width: 100.0,
height: 100.0,
color: Colors.deepPurple,
child: DragTarget(
builder: (context, List<int> candidateData, rejectedData) {
return Center(
child: Text(
"Odd",
style: TextStyle(color: Colors.white, fontSize: 22.0),
));
},
onWillAccept: (data) {
return true;
},
onAccept: (data) {
if (data % 2 != 0) {
setState(() {
score++;
});
if (score >= 10) {
showDialog(
context: context,
builder: (BuildContext context) {
return new AlertDialog(
title: Text("Congrats!!"),
content: Text("No-brainer...😮"),
actions: <Widget>[
FlatButton(
child: Text("Thanks"),
onPressed: () {
Navigator.of(context).pop();
setState(() {
score = 0;
});
},
)
],
);
});
}
} else {
setState(() {});
}
},
),
)
],
)
],
),
),
);
}
}
If you need to drop at a non-fixed location (Draggable without a DragTarget), this can also be implemented with Stack()/Positioned() using renderbox sizing, as per How to move element anywhere inside parent container with drag and drop in Flutter?