How to reflect the value from FutureProvider when certain UI onPressed? - flutter

I'm very new about Flutter and the library reiver_pod.
I want show Text("Hello World") on screen, only when floatingActionButton pressed with using FutureProvider but it's always shown even though the button has never been pressed ? How it come and how can I Fix it ?
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'FutureProvider',
theme: ThemeData(
textTheme: const TextTheme(bodyText2: TextStyle(fontSize: 50)),
),
home: HomePage(),
);
}
}
final futureProvider = FutureProvider<dynamic>((ref) async {
await Future.delayed(const Duration(seconds: 3));
return 'Hello World';
});
class HomePage extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
final asyncValue = ref.watch(futureProvider);
return Scaffold(
appBar: AppBar(title: const Text('TEST')),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.refresh),
onPressed: () {
ref.refresh(futureProvider);
},
),
body: Center(
child: asyncValue.when(
error: (err, _) => Text(err.toString()),
loading: () => const CircularProgressIndicator(),
data: (data) {
print(data);
return Text(data.toString());//here
},
),
),
);
}
}
renewal code as follow:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:math';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'FutureProvider',
theme: ThemeData(
textTheme: const TextTheme(bodyText2: TextStyle(fontSize: 50)),
),
home: HomePage(),
);
}
}
final StateProvider<bool> pressProvider = StateProvider((ref) => false);
final futureProvider = FutureProvider<dynamic>((ref) async {
var intValue = Random().nextInt(100);
await Future.delayed(const Duration(seconds: 1));
return intValue.toString();
});
class HomePage extends ConsumerWidget {
#override
Widget build(BuildContext context, WidgetRef ref) {
return Scaffold(
appBar: AppBar(title: const Text('TEST')),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.refresh),
onPressed: () {
ref.read(pressProvider.notifier).update((state) => true);
ref.refresh(futureProvider);
},
),
body: Center(
child: ref.watch(pressProvider)
? Consumer(
builder: (context, ref, child) {
final asyncValue = ref.watch(futureProvider);
return asyncValue.when(
error: (err, _) => Text(err.toString()),
loading: () => const CircularProgressIndicator(),
data: (data) {
return Text(data.toString()); //here
},
);
},
)
: null),
);
}
}

You can use a bool to handle tap event like, FutureProvider will handle the UI update case.
class HomePage extends ConsumerWidget {
bool isPressed = false;
#override
Widget build(BuildContext context, WidgetRef ref) {
final asyncValue = ref.watch(futureProvider);
return Scaffold(
appBar: AppBar(title: const Text('TEST')),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.refresh),
onPressed: () {
isPressed = true;
ref.refresh(futureProvider);
},
),
body: Center(
child: isPressed
? asyncValue.when(
error: (err, _) => Text(err.toString()),
loading: () => const CircularProgressIndicator(),
data: (data) {
print(data);
return Text(data.toString()); //here
},
)
: null,
),
);
}
}

Related

Flutter build not behaving as expected

I'm trying to make a note app but there is a yellow square showing on the screen.
I've included the main.dart code and also allnotesscreens.dart. I think there is something wrong with allnotesscreens code, but I don't know what.
Maybe _loadViewMode() part.
Why this problem is happening?!!!
Main.dart:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'providers/label_provider.dart';
import 'providers/note_provider.dart';
import 'package:provider/provider.dart';
import 'constants/app_constants.dart';
import 'screens/all_labels_screen.dart';
import 'screens/all_notes_screen.dart';
import 'screens/drawer_screen.dart';
main() {
SystemChrome.setSystemUIOverlayStyle(
const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
systemNavigationBarColor: ColorsConstant.grayColor,
),
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => NoteProvider()),
ChangeNotifierProvider(create: (_) => LabelProvider()),
],
builder: (context, child) => MaterialApp(
title: 'Note-App',
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.dark,
theme: customThemeData(context),
initialRoute: '/',
routes: {
'/': (context) => const AllNotesScreen(),
DrawerScreen.routeName: (context) => const DrawerScreen(),
AllLabelsScreen.routeName: (context) => const AllLabelsScreen(),
},
),
);
}
}
allnotesscreens.dart:
class AllNotesScreen extends StatefulWidget {
const AllNotesScreen({Key? key}) : super(key: key);
#override
State<AllNotesScreen> createState() => _AllNotesScreenState();
}
class _AllNotesScreenState extends State<AllNotesScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child:
Container(
height: 200,
width: 100,
color: Colors.yellow,
),
),
);
}
String _viewMode = ViewMode.staggeredGrid.name;
bool _isLoading = false;
final _scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
setState(() {
_isLoading = true;
});
}
#override
void didChangeDependencies() {
super.didChangeDependencies();
Future _loadViewMode() async {
final prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey('view-mode')) return;
setState(() {
_viewMode = prefs.getString('view-mode') ?? ViewMode.staggeredGrid.name;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text(
"all notes",
style: TextStyleConstants.titleAppBarStyle,
),
actions: [
if (context
.watch<NoteProvider>()
.items
.isNotEmpty)
IconButton(
onPressed: () {
showSearch(
context: context,
delegate: NoteSearch(isNoteByLabel: false),
);
},
icon: const Icon(Icons.search),
),
IconButton(
onPressed: () async {
final result = await changeViewMode(_viewMode);
setState(() {
_viewMode = result;
});
},
icon: _viewMode == ViewMode.staggeredGrid.name
? const Icon(Icons.view_stream)
: const Icon(Icons.grid_view),
),
const SizedBox(
width: 6,
)
],
),
drawer: const DrawerScreen(),
body: _isLoading
? const Center(
child: CircularProgressIndicator(),
)
: RefreshIndicator(
onRefresh: () => refreshOrGetData(context),
child: Consumer<NoteProvider>(
builder: (context, noteProvider, child) =>
noteProvider.items.isNotEmpty
? NoteListViewWidget(
notes: noteProvider.items,
viewMode: _viewMode,
scaffoldContext: _scaffoldKey.currentContext!,
)
: child!,
child: const NoNoteUIWidget(
title: "your notes after adding will appear here",
),
),
),
floatingActionButton: FloatingActionButton(
child: linearGradientIconAdd,
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const EditNoteScreen(),
));
},
),
);
}
}
}
The first few lines of your _AllNotesScreenState class are why there's a yellow square; that's what you're telling it to build.
class _AllNotesScreenState extends State<AllNotesScreen> {
// this build function here is what is drawing to the screen
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child:
Container(
height: 200,
width: 100,
color: Colors.yellow,
),
),
);
}
Maybe it's just how you've pasted it in, but it appears as though you have a build function defined within the didChangeDependencies function. If you took it out of there, it would then make it apparent that you have two build functions defined for the class.
I'm assuming it's the second one that you actually want building.
#override
void didChangeDependencies() {
super.didChangeDependencies();
Future _loadViewMode() async {
final prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey('view-mode')) return;
setState(() {
_viewMode = prefs.getString('view-mode') ?? ViewMode.staggeredGrid.name;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
...

values in alert dialog not update from outside

I need to show a progress bar like below :
I have implemented it this way but the value inside the progress bar is not updated :
use upload() function to simulate file upload by submitting a test POST request
use StatefulBuilder() for convert my dialog from stateless to statefull
my code :
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(const MaterialApp(
home: MyApp(),
));
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String status = "loading ...";
double uploadedPercent = 0.0;
Future<void> upload() async {
final response = await http.post(
Uri.parse('https://jsonplaceholder.typicode.com/albums'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'title': "test",
}),
);
if (response.statusCode == 201) {
// If the server did return a 201 CREATED response,
// then parse the JSON.
setState(() {
status = "Uploaded";
uploadedPercent = 1.0;
});
debugPrint(status);
} else {
throw Exception('Failed to create album.');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Upload File'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () {
showDialog(
context: context,
builder: ((BuildContext context) {
return StatefulBuilder(builder: (context, setState) {
return AlertDialog(
title: Text(status),
content: LinearProgressIndicator(
value: uploadedPercent,
backgroundColor: Colors.grey,
color: Colors.green,
minHeight: 10,
),
);
});
}),
);
upload();
},
child: const Text('Upload'),
),
],
),
),
// buildFutureBuilder(),
);
}
}
my
status variable
and
uplaodPercent variable
not be update in alert dialog and the my LinearProgressBar() stay in this state :
I conducted an experiment, its essence was to put the setState (() {}); method inside the StatefulBuilder and periodically call it to redraw the AlertBox widget, everything worked out for me, here is an example:
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("App bar"),
),
body: HelpSO(count: 0.1),
));
}
}
class HelpSO extends StatelessWidget {
double count;
HelpSO({Key? key, required this.count}) : super(key: key);
#override
Widget build(BuildContext context) {
return StatefulBuilder(builder:
(BuildContext context, void Function(void Function()) setState) {
Timer.periodic(const Duration(seconds: 2), (Timer t) {
setState(() {
count += 0.1;
print(count);
});
});
return AlertDialog(
key: ValueKey(count),
title: const Text("Loading..."),
content: LinearProgressIndicator(
value: count,
backgroundColor: Colors.grey,
color: Colors.green,
minHeight: 10,
),
);
});
}
}
So your problem is that everything works for you, but the widget is not redrawn, so try to put the setState(() {}); in StatefulBuilder and call it at the moment when your widget needs to be updated.

Flutter Provider.of<> without a consumer don't change my state

I am trying to get into the provider topic, however calling a function only works if I put it into a consumer
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ClickerProvider()),
],
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Text("some text"),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Provider.of<ClickerProvider>(context, listen: false)
.incrementCounter(),
tooltip: 'Increment',
child: Icon(Icons.add),
),
));
}
As in this example, my state is not updated. However, it already works with a consumer.
floatingActionButton: Consumer<ClickerProvider>(
builder: (context, value, child) {
return FloatingActionButton(
onPressed: Provider.of<ClickerProvider>(context, listen: false)
.incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
);
},
)
Is there an error in my code?
You can copy paste run two full code below
Reason : Can not find ClickerProvider
Solution 1: Move ClickerProvider to upper level such as MyApp
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ClickerProvider()),
],
child: MaterialApp(
Solution 2: Use Builder
body: Center(child: Builder(builder: (BuildContext context) {
return Text(context.watch<ClickerProvider>().getCounter.toString());
})),
floatingActionButton: Builder(builder: (BuildContext context) {
return FloatingActionButton(
onPressed: () =>
Provider.of<ClickerProvider>(context, listen: false)
.incrementCounter(),
tooltip: 'Increment',
child: Icon(Icons.add),
);
full code 1
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ClickerProvider extends ChangeNotifier {
int _count = 0;
int get getCounter {
return _count;
}
void incrementCounter() {
_count += 1;
notifyListeners();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ClickerProvider()),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
));
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Text(context.watch<ClickerProvider>().getCounter.toString()),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Provider.of<ClickerProvider>(context, listen: false)
.incrementCounter(),
tooltip: 'Increment',
child: Icon(Icons.add),
));
}
}
full code 2
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ClickerProvider extends ChangeNotifier {
int _count = 0;
int get getCounter {
return _count;
}
void incrementCounter() {
_count += 1;
notifyListeners();
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ClickerProvider()),
],
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(child: Builder(builder: (BuildContext context) {
return Text(context.watch<ClickerProvider>().getCounter.toString());
})),
floatingActionButton: Builder(builder: (BuildContext context) {
return FloatingActionButton(
onPressed: () =>
Provider.of<ClickerProvider>(context, listen: false)
.incrementCounter(),
tooltip: 'Increment',
child: Icon(Icons.add),
);
}),
));
}
}
As you can refer in the source code of Consumer here:
Obtains [Provider] from its ancestors and passes its value to [builder].
The [Consumer] widget doesn't do any fancy work. It just calls [Provider.of]
in a new widget, and delegates its build implementation to [builder].
Provider.of<X> depends on value of listen (true or false) to trigger new State.build() to widgets and State.didChangeDependencies() for StatefulWidget.
Consumer<X> always update UI, as it uses Provider.of<T>(context), where listen is true
In this case, since your listen is set as false, but you're putting it in the Consumer which make it true. That's why the UI will update with Consumer

How can i pass parameters in flutter if the user goes back with the arrow?

I know that if you had a raiseButton you can do
Navigation .... .pop(value);
But what happens if the user goes back and i want to update the value, because result will be null
Navigator.push(context, MaterialPageRoute(builder: (context) {
return GalleryClassOne();
})).then((result) {
if (result != null) {
setState(() {
imagesClas1 = result;
});
}
});
You can override the back button behavior with WillPopScope widget. And manually pop with the data you need. Here is the code:
import 'package:flutter/material.dart';
void main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: Navigator(
onGenerateRoute: (settings) => MaterialPageRoute(
builder: (context) => MyHomePage(),
),
),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _onButtonPressed() {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) => OtherPage()))
.then((value) {
print("returned: $value");
if (value != null) {
setState(() {
// ...
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Demo")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Text("Open another screen"),
onPressed: _onButtonPressed),
],
),
),
);
}
}
class OtherPage extends StatelessWidget {
OtherPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// here you can return anything you need ...
Navigator.of(context).pop("my value");
// cancel default behaviour
return false;
},
child: Scaffold(
appBar: AppBar(title: Text("Other page")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Click on return button'),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
),
);
}
}
You should return your data at a variable like this
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SelectionScreen()),
);
The result variable has your data.
for more info, have a look at the docs

How do I add floatingactionbutton in my ListView in Flutter dart

I want to add a floatingactionbutton in my ListPage on the bottom right corner.
I tried adding it but I am getting error or it is becoming a dead code.
An on press will be implemented on that floatingactionbutton to create a user and that will be reflected in the listview page.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(new AdminPage());
class AdminPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Admin Dashboard',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Admin Dashboard'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context){
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: ListPage(),
);
}
}
class ListPage extends StatefulWidget {
#override
_ListPageState createState() => _ListPageState();
}
class _ListPageState extends State<ListPage> {
Future _data;
Future getPosts() async {
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore.collection("admins").getDocuments();
return qn.documents;
}
#override
Widget build(BuildContext context) {
Future getPosts() async {
var firestore = Firestore.instance;
QuerySnapshot qn = await firestore.collection("admins").getDocuments();
return qn.documents;
}
navigateToDetail(DocumentSnapshot post){
Navigator.push(context, MaterialPageRoute(builder: (context) => DetailPage(post: post,)));
}
#override
void initState(){
super.initState();
_data = getPosts();
}
return Container(
child: FutureBuilder(
future: _data,
builder: (_, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: Text("Loading..."),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, index){
return ListTile(
title: Text(snapshot.data[index].data["email"]),
onTap: () => navigateToDetail(snapshot.data[index]),
);
});
}
}),
);
}
}
class DetailPage extends StatefulWidget {
final DocumentSnapshot post;
DetailPage({this.post});
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title : Text(widget.post.data["name"]),
),
body: Container(
child:Card(
child: ListTile(
title:Text(widget.post.data["email"]),
subtitle: Text(widget.post.data["name"]),
),
),
),
);
}
}
Image of the screen can be found below
You can add floatingActionButton argument on Scaffold
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: ListPage(),
floatingActionButton: FloatingActionButton(
onPressed: () =>{},
child: const Icon(Icons.add),
),
);
You can add FAB in listview by wrapping FloatingActionButton inside of Transform.translate:
floatingActionButton:Transform.translate(
offset: const Offset(-10, -70),
child: FloatingActionButton(
onPressed: () =>{},
child: const Icon(Icons.add),
),
),