I want to use a variable, which i want to fill with firebase data.
In the Firebase section it fills it up with the correct data and print it correctly to log, but after that in the Scaffold its looks like it using the data in the starting declaration.
But why? It's one variable with two data? So at the end of the code in the Text('$masik') i want to use the firebase data, not the starting string.
What am i doing wrong?
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String masik = '';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase',
home: AddData(),
);
}
}
class AddData extends StatelessWidget {
#override
Widget build(BuildContext context) {
String doksi = 'valami más';
var ezlett;
String masik = 'minden';
String sajt = '';
final Stream<QuerySnapshot> collectionStream = FirebaseFirestore.instance.collection('zenek').snapshots();
FirebaseFirestore.instance
.collection('zenek')
.doc(doksi)
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
String ezegyszoveg = documentSnapshot.data().toString();
print('Document exists on the database $ezegyszoveg');
ezlett = ezegyszoveg.substring(9, ezegyszoveg.indexOf(','));
print(ezlett);
masik = ezegyszoveg.substring(ezegyszoveg.indexOf('text: ')+6, ezegyszoveg.indexOf('}'));
print(masik);
}
});
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
title: const Text("próba"),
),
body:Row(
children: [
Text('$masik'),
],
)
);
}
}
Something like this example will work to update the UI when there is new data has been updated
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String masik = '';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Firebase',
home: AddData(),
);
}
}
class AddData extends StatefulWidget {
#override
State<AddData> createState() => _AddDataState();
}
class _AddDataState extends State<AddData> {
String doksi = 'valami más';
var ezlett;
String masik = 'minden';
String sajt = '';
#override
void initState() {
super.initState();
updateTheUi();
}
void updateTheUi() {
final Stream<QuerySnapshot> collectionStream =
FirebaseFirestore.instance.collection('zenek').snapshots();
FirebaseFirestore.instance
.collection('zenek')
.doc(doksi)
.get()
.then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
String ezegyszoveg = documentSnapshot.data().toString();
ezlett = ezegyszoveg.substring(9, ezegyszoveg.indexOf(','));
masik = ezegyszoveg.substring(
ezegyszoveg.indexOf('text: ') + 6, ezegyszoveg.indexOf('}'));
setState(() {});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
title: const Text("próba"),
),
body: Row(
children: [
Text('$masik'),
],
));
}
}
hey u r doing a silly mistake u have to use setState when u update your variable :
setState(){
masik = ezegyszoveg.substring(ezegyszoveg.indexOf('text: ')+6,ezegyszoveg.indexOf('}'));
} // this line put in setState in your code
Related
I have a serious problem with my Riverpod. Specifically, I am using StateProvider in Riverpod package. But when I update state, the widget tree does not rebuild. I checked the new state whether is updated by printing out state to see, I see that they are actually updated.
I have some same situations but when I click hot restart/reload page/scroll up,down mouse to change size chrome window, the widget tree rebuild one time.
Please help me and explain everything the most detail and easy to understand. Thank you very much
new state print out but UI not update
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:math';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class Data {
final String data;
Data({required this.data});
}
final helloWorldProvider = StateProvider<Data?>((ref) => Data(data: 'No data'));
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
#override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
#override
void initState() {
// TODO: implement initState4
print("Init state");
super.initState();
// getData();
}
// getData() async {
// // http.Response response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
// // final title = jsonDecode(response.body)["title"];;
// // ref.read(helloWorldProvider.notifier).update((state) => title);
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('valueTemp', 'newValue');
// String? valueTemp = prefs.getString('valueTemp');
// String value = valueTemp ?? '';
// Data data = Data(data: value);
// ref.read(helloWorldProvider.notifier).update((state) => data);
// print("Đã thực hiện xong");
// }
void _change() {
print("change");
final rawString = generateRandomString(5);
Data data = new Data(data: rawString);
ref.watch(helloWorldProvider.notifier).update((state) => data);
print(ref.read(helloWorldProvider.notifier).state?.data);
}
String generateRandomString(int len) {
var r = Random();
return String.fromCharCodes(List.generate(len, (index) => r.nextInt(33) + 89));
}
#override
Widget build(BuildContext context) {
print('Rebuild');
final data = ref.watch(helloWorldProvider.notifier).state;
final dataText = data?.data ?? 'No text';
print(dataText);
return MaterialApp(
title: 'Google Docs Clone',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Column(children: [
Text(dataText)
]
)
),
floatingActionButton: FloatingActionButton(
onPressed: _change,
tooltip: 'Change',
child: const Icon(Icons.add),
),
));
}
}
I don't want to use other pattern as Provider, Bloc, StateNotifierProvider, ChangeNotifierProvider... I only want to run StateProvider successfully. I have refered to many articles and stackoverflows answer but I did't found any useful helps to my case.
final data = ref.watch(helloWorldProvider.notifier).state;
is watching the notifier, which rarely changes. You want to watch the state change, as in:
final data = ref.watch(helloWorldProvider);
Fixed, Tested your code.
I recommend this article Flutter Riverpod 2.0: The Ultimate Guide to Advanced Riverpod 😀
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'dart:math';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class Data {
final String data;
Data({required this.data});
}
final helloWorldProvider = StateProvider<Data?>((ref) => Data(data: 'No data'));
class MyApp extends ConsumerStatefulWidget {
const MyApp({super.key});
#override
ConsumerState<MyApp> createState() => _MyAppState();
}
class _MyAppState extends ConsumerState<MyApp> {
#override
void initState() {
// TODO: implement initState4
print("Init state");
super.initState();
// getData();
}
// getData() async {
// // http.Response response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos/1'));
// // final title = jsonDecode(response.body)["title"];;
// // ref.read(helloWorldProvider.notifier).update((state) => title);
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString('valueTemp', 'newValue');
// String? valueTemp = prefs.getString('valueTemp');
// String value = valueTemp ?? '';
// Data data = Data(data: value);
// ref.read(helloWorldProvider.notifier).update((state) => data);
// print("Đã thực hiện xong");
// }
void _change() {
print("change");
final rawString = generateRandomString(5);
Data data = Data(data: rawString);
ref.read(helloWorldProvider.notifier).update((state) => data);
print(ref.read(helloWorldProvider.notifier).state?.data);
}
String generateRandomString(int len) {
var r = Random();
return String.fromCharCodes(
List.generate(len, (index) => r.nextInt(33) + 89));
}
#override
Widget build(BuildContext context) {
print('Rebuild');
final data = ref.watch(helloWorldProvider)?.data;
final dataText = data ?? 'No text';
print(dataText);
return MaterialApp(
title: 'Google Docs Clone',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Center(
child: Column(children: [Text(dataText)]),
),
floatingActionButton: FloatingActionButton(
onPressed: _change,
tooltip: 'Change',
child: const Icon(Icons.add),
),
),
);
}
}
I am new to Flutter, and bloc too. I got the idea, how bloc works. But When I create a simple app as the first step of my note app. The bloc doesn't give the data. This simple app has two screens. list screen and Notedetailscreen. Button in NoteDetailScreen tapped, data does not print to the text widget.
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
import 'package:note_demo_bloc/list_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return BlocProvider<NoteBloc>(
create: (context) => NoteBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ListScreen(),
),
);
}
}
note_bloc.dart
import 'dart:async';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:meta/meta.dart';
part 'note_event.dart';
part 'note_state.dart';
class NoteBloc extends Bloc<NoteblocEvent, NoteblocState> {
NoteBloc() : super(NoteblocInitial());
#override
Stream<NoteblocState> mapEventToState(
NoteblocEvent event,
) async* {
if (event == NoteSaveEvent) {
yield NoteSaveState(state);
}
}
}
part of 'note_bloc.dart';
#immutable
abstract class NoteblocEvent {}
class NoteSaveEvent extends NoteblocEvent {
NoteSaveEvent(this.text);
final text;
}
note_state.dart
part of 'note_bloc.dart';
#immutable
abstract class NoteblocState {}
class NoteblocInitial extends NoteblocState {}
class NoteSaveState extends NoteblocState {
NoteSaveState(this.text);
final text;
}
list_screen.dart
import 'package:flutter/material.dart';
import 'package:note_demo_bloc/note_detail_screen.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
_ListScreenState createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('hi'),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => NoteDetailScreen(),
),
);
},
),
);
}
}
Note_detailscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
class NoteDetailScreen extends StatefulWidget {
const NoteDetailScreen({Key? key}) : super(key: key);
#override
_NoteDetailScreenState createState() => _NoteDetailScreenState();
}
class _NoteDetailScreenState extends State<NoteDetailScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
BlocProvider.of<NoteBloc>(context).add(NoteSaveEvent('hi'));
},
child: Text('click'),
),
BlocBuilder<NoteBloc, NoteblocState>(
builder: (context, state) {
return Text(state.toString());
},
)
],
),
);
}
}
Your bloc, state, and event looks fine. When you push screen you might need to use BlocProvider again. So try this:
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
import 'package:note_demo_bloc/list_screen.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
NoteBloc _noteBloc = NoteBloc();
#override
Widget build(BuildContext context) {
return BlocProvider<NoteBloc>(
create: (context) => _noteBloc(),
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ListScreen(),
),
);
}
}
list_screen.dart
import 'package:flutter/material.dart';
import 'package:note_demo_bloc/note_detail_screen.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
_ListScreenState createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Text('hi'),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => BlocProvider.value(value: BlocProvider.of<NoteBloc>(context), child: NoteDetailScreen()),
),
);
},
),
);
}
}
Note_detailscreen.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:note_demo_bloc/bloc/note_bloc.dart';
class NoteDetailScreen extends StatefulWidget {
const NoteDetailScreen({Key? key}) : super(key: key);
#override
_NoteDetailScreenState createState() => _NoteDetailScreenState();
}
class _NoteDetailScreenState extends State<NoteDetailScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
ElevatedButton(
onPressed: () {
BlocProvider.of<NoteBloc>(context).add(NoteSaveEvent('hi'));
},
child: Text('click'),
),
BlocBuilder<NoteBloc, NoteblocState>(
bloc: BlocProvider.of<NoteBloc>(context),
builder: (context, state) {
return Text(state.toString());
},
)
],
),
);
}
}
So, this is not an answer of your question but consider that as alternative (for future users of SO).
As state management is a free choice, and everyone could manage that as it’s “modus operandi“ this helper class “home made” could be a good choice.
import 'dart:async';
import 'dart:core';
class Method {
Method(this.name, this.params);
final String name;
final Map<String, Object> params;
}
class _Controller {
_Controller._();
static final Map<String, _Controller> _this = new Map<String, _Controller>();
final Map<String, Function(Method)> _funcs = new Map<String, Function(Method)>();
factory _Controller(String identifier) => _this.putIfAbsent(identifier, () => _Controller._());
Future<void> activateListener(String listenerId, Function(Method) function) async {
if (function != null)
_funcs.containsKey(listenerId) ? _funcs[listenerId] = function : _funcs.putIfAbsent(listenerId, () => function);
}
Future<void> deactivateListener(String listenerId) async =>
_funcs.removeWhere((String key, Function(Method) func) => key == listenerId);
Future<void> removeListener(String identifier) async =>
_this.removeWhere((String key, _Controller mClass) => key == identifier);
Future<void> callMethod(String methodName, {Map<String, Object> params}) async =>
Future.forEach(_funcs.values.where((v) => v != null), (func) async => func.call(Method(methodName, params)));
}
mixin MethodListener on Object {
_Controller _getController(String identifier) => _Controller(identifier ?? this.runtimeType.toString());
Future<void> activateListener({String identifier, List<String> identifiers}) async {
if (identifiers != null && identifiers.length > 0)
identifiers.forEach(
(String currentId) => _getController(currentId).activateListener(this.hashCode.toString(), onMethodListener));
else
_getController(identifier).activateListener(this.hashCode.toString(), onMethodListener);
}
Future<void> deactivateListener({String identifier, List<String> identifiers}) async {
if (identifiers != null && identifiers.length > 0)
identifiers.forEach((String currentId) => _getController(currentId).deactivateListener(this.hashCode.toString()));
else
_getController(identifier).deactivateListener(this.hashCode.toString());
}
Future<void> removeListener({String identifier}) async => _getController(identifier).removeListener(identifier);
void onMethodListener(Method method) async => null;
Future<void> callMethodOn(String identifier, String methodName, {Map<String, Object> params}) async =>
_getController(identifier).callMethod(methodName, params: params);
}
class MethodManager with MethodListener {
MethodManager._();
static MethodManager _this;
factory MethodManager() {
if (_this == null) _this = MethodManager._();
return _this;
}
Future<void> callMethodOnWidgets(List<String> identifiers, String methodName, {Map<String, Object> params}) async =>
identifiers.forEach((String currentId) => callMethodOn(currentId, methodName, params: params));
#override
Future<void> callMethodOn(String identifier, String methodName, {Map<String, Object> params}) async =>
super.callMethodOn(identifier, methodName, params: params);
}
then you can implements classes with “with MethodListener” as follows:
import 'package:flutter/material.dart';
import 'package:yourpackagehere/utils/XMethods.dart';
class Test extends StatefulWidget {
static const String NAME = "Test";
#override
createState() => _TestState();
}
class _TestState extends State<Test> with MethodListener {
String _ciao;
#override
void initState() {
super.initState();
this.activateListener(identifier: Test.NAME);
}
#override
void dispose() {
this.deactivateListener(identifier: Test.NAME);
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(child: Text(_ciao));
}
#override
void onMethodListener(Method method) {
switch (method.name) {
case "say_hello":
if (mounted) {
setState(() {
_ciao = method.params["my_string"];
});
}
break;
}
}
}
Usage:
From everywhere (from widgets or classes):
MethodManager().callMethodOn(Test.NAME, "say_hello", params: {"my_string": "SIAMO CAMPIONI DI EUROPA!!!"});
When I run main.dart on my real phone code runs without any mistakes, I can see the string value exactly on my Location page but when I adjust my android emulator phone's location Turkey/İstanbul it stuckes on spinner (Loading page), Spinner keeps turning forever so Location page never open, by the way "I/eatherforecast(19501): Waiting for a blocking GC ProfileSaver" error written on console. I really wonder the reason of this tedious issue. Have a nice day..
import 'package:flutter/material.dart';
import 'package:weatherforecast2/loadingpage.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData.dark().copyWith(),
home: LoadingPage(),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:weatherforecast2/locationpage.dart';
import 'locationfinder.dart';
import 'package:geolocator/geolocator.dart';
import 'network.dart';
class LoadingPage extends StatefulWidget {
static String id = "loadingpage";
#override
_LoadingPageState createState() => _LoadingPageState();
}
class _LoadingPageState extends State<LoadingPage> {
Map<String,dynamic> currentLocationWeatherData;
void initState(){
super.initState();
getDecodedCurrentLocationWeatherData();
}
void getDecodedCurrentLocationWeatherData()async{
Position position=await LocationFinder().getCurrentLocation();
currentLocationWeatherData = await NetworkHelper().getCurrentLocationWeather(position.latitude,
position.longitude);
Navigator.push(context, MaterialPageRoute(builder: (context) {
return LocationPage(
currentLocationWeatherData: currentLocationWeatherData,
);
}));
}
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child:SpinKitFadingCube(
color: Colors.white,
size: 50,
),
),
);
}
}
import 'dart:convert';
import 'package:http/http.dart';
const String appid="57aad03f4e48ca815bb1184e74624f46";
const String openWeatherMapURL="https://api.openweathermap.org/data/2.5/weather";
class NetworkHelper{
Future<dynamic>getCurrentLocationWeather(lat,lon)async{
Response response=await get("https://api.openweathermap.org/data/2.5/weather?
lat=$lat&lon=$lon&appid=$appid");
if (response.statusCode == 200){
return jsonDecode(response.body);
}
}
}
import 'package:flutter/material.dart';
class LocationPage extends StatefulWidget {
LocationPage({#required this.currentLocationWeatherData});
final Map<String,dynamic> currentLocationWeatherData;
static String id = "locationpage";
#override
_LocationPageState createState() => _LocationPageState();
}
class _LocationPageState extends State<LocationPage> {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Center(
child: Text(
widget.currentLocationWeatherData["weather"][0]["description"],
),
),
);
}
}
import 'package:geolocator/geolocator.dart';
class LocationFinder{
Future<Position> getCurrentLocation()async{
Position position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.low);
return position;
}
}
I am wanting to repeat the handleImages() method every five seconds but having an issue getting this to work.
import 'package:flutter/material.dart';
class CameraImageWidget extends StatefulWidget {
const CameraImageWidget({
Key key,
}) : super(key: key);
#override
_CameraImageWidgetState createState() => _CameraImageWidgetState();
}
class _CameraImageWidgetState extends State<CameraImageWidget> {
String handleImages() {
List<String> cameraImages = [
'assets/images/imageUpload.gif',
'assets/images/imageUploadGreen.gif',
];
String randomImage = (cameraImages..shuffle()).first;
return randomImage;
}
#override
Widget build(BuildContext context) {
return Image.asset(handleImages(), height: 450.0);
}
}
This should do the trick. Have not tested it tho.
class _CameraImageWidgetState extends State<CameraImageWidget> {
String _image;
#override
initState(){
handleImages();
}
void handleImages() async{
List<String> cameraImages = [
'assets/images/imageUpload.gif',
'assets/images/imageUploadGreen.gif',
];
setState(() {
_image = (cameraImages..shuffle()).first;
});
//Wait 5 seconds
await new Future.delayed(const Duration(seconds:5));
handleImages();
}
#override
Widget build(BuildContext context) {
return Image.asset(_image, height: 450.0);
}
}
import 'dart:async';
import 'package:flutter/material.dart';
class CameraImageWidget extends StatefulWidget {
const CameraImageWidget({
Key key,
}) : super(key: key);
#override
_CameraImageWidgetState createState() => _CameraImageWidgetState();
}
class _CameraImageWidgetState extends State<CameraImageWidget> {
String handleImages() {
String chosenImage;
List<String> cameraImages = [
'assets/images/imageUpload.gif',
'assets/images/imageUploadGreen.gif',
];
Timer.periodic(Duration(seconds: 10), (timer) {
setState(
() {
chosenImage = (cameraImages..shuffle()).first;
},
);
});
return chosenImage;
}
#override
Widget build(BuildContext context) {
return Image.asset(handleImages(), height: 400.0);
}
}
I made a dartpad.dev of the following code so that you can run it yourself. (however, instead of images, I am just showing the "asset text" every 5 seconds since it's on dartpad. You would just have to insert the text into the Image.asset using Image.asset(randomImage, height: 450.0);
Here's the dartpad: http://dartpad.dev/2af9c2d7c4b3436dfa55348bb4fb9dca
and here's the code:
import 'package:flutter/material.dart';
import 'dart:async';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CameraImageWidget(),
);
}
}
class CameraImageWidget extends StatefulWidget {
const CameraImageWidget({
Key key,
}) : super(key: key);
#override
_CameraImageWidgetState createState() => _CameraImageWidgetState();
}
class _CameraImageWidgetState extends State<CameraImageWidget> {
static List<String> cameraImages = [
'assets/images/imageUpload.gif',
'assets/images/imageUploadGreen.gif',
];
String randomImage = (cameraImages..shuffle()).first;
int count = 0;
#override
initState() {
handleImages();
}
handleImages() {
Timer.periodic(Duration(seconds: 5), (timer) {
setState(() {
randomImage = (cameraImages..shuffle()).first;
print(randomImage);
count++;
print(count);
});
});
}
#override
Widget build(BuildContext context) {
// return Image.asset(handleImages(), height: 450.0);
return Text(randomImage); //
}
}
I am learning Flutter. I wrote small app to getting key from API and print it on screen. The problem is that my getApiKey() method is looping.
Why? And How I can prevent it?
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:provider/provider.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: ChangeNotifierProvider<TenderApiData>(
builder: (_) => TenderApiData(), child: HomePage()),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(), body: MyContainer());
}
}
class MyContainer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[MyTestWidget()],
);
}
}
class TenderApiData with ChangeNotifier {
String access_token;
String url = "https://";
getApiKey() async
{
var response = await http.post(url, headers: {"Accept": "application/json"});
// await Future.delayed(Duration(seconds: 25));
if (response.statusCode == 200)
{
access_token = json.decode(response.body)['access_token'];
notifyListeners();
}
}
}
class MyTestWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
Provider.of<TenderApiData>(context).getApiKey();
var result = Provider.of<TenderApiData>(context).access_token;
return Row(
children: <Widget>[
Flexible(child: Text("Data: $result"))
],
);
}
}
The reason this happens is because you are notifying listeners in your getApiKey function and then calling getApiKey in your build method. The build method is called when you notify your listeners, see why this loops?
Anyways, to prevent it, you simply convert your StatelessWidget to a StatefulWidget and only call getApiKey in State.didChangeDependencies (not in initState because you need access to the BuildContext):
class MyTestWidget extends StatefulWidget {
#override
_MyTestWidgetState createState() => _MyTestWidgetState();
}
class _MyTestWidgetState extends State<MyTestWidget> {
bool apiKeyLoaded;
#override
void initState() {
apiKeyLoaded = false;
super.initState();
}
#override
void didChangeDependencies() {
if (!apiKeyLoaded) {
Provider.of<TenderApiData>(context).getApiKey();
apiKeyLoaded = true;
}
super.didChangeDependencies();
}
#override
Widget build(BuildContext context) {
var result = Provider
.of<TenderApiData>(context)
.access_token;
return Row(
children: <Widget>[
Flexible(child: Text("Data: $result"))
],
);
}
}