I have created following 2 streams.
Stream 1 - Periodic Stream which is calling an async function at a particular time.
Stream 2 - Function which listens on stream 1 and then create stream 2
I wanted to add logic such that if stream 1 length is more that 0 then only create stream 2. But I am getting length of stream 1 as always 0.
My Code is as follows. (It runs after hot reload)
import 'dart:async';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
int _counter = 0;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyPage(),
);
}
}
class MyPage extends StatefulWidget {
#override
_MyPageState createState() => _MyPageState();
}
class _MyPageState extends State<MyPage> {
StreamController<List<String>> _myStreamController =
StreamController<List<String>>();
int _counter = 0;
Stream<List<String>> _stream() {
Duration interval = Duration(milliseconds: 100);
Stream<List<String>> stream =
Stream<List<String>>.periodic(interval, temp2);
return stream;
}
Future temp() async {
List<String> a = [];
_counter++;
a.add(_counter.toString());
return a;
}
List<String> temp2(int value) {
List<String> _localMsg = List();
temp().then((a) {
print(a);
_localMsg = a;
});
return _localMsg;
}
#override
void initState() {
super.initState();
_stream().listen((ondata) {
print(ondata);
if (ondata.length > 0) _myStreamController.sink.add(ondata);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
child: StreamBuilder(
stream: _myStreamController.stream,
builder: (context, snapshot) {
if (snapshot.data != null) {
return Text(snapshot.data.toString());
} else {
return Text("waiting...");
}
},
),
),
),
);
}
}
Related
I am trying to make a singleton of sharedpreferences, but I get a failure of not initialized
(LateError (LateInitializationError: Field 'prefs' has not been initialized.)).
I don't know what could be wrong. I suppose that it could be for the late of SharedPreferences.dart, or it lacks somewhere to define the initialization?
Personaje_home.dart
class PersonajeHomePage extends StatefulWidget {
const PersonajeHomePage({Key? key}) : super(key: key);
#override
State<PersonajeHomePage> createState() => _PersonajeHomePageState();
}
class _PersonajeHomePageState extends State<PersonajeHomePage> {
#override
Widget build(BuildContext context) {
var estiloTexto = EstiloTextos();
final proPersonaje = Provider.of<PersonajeProvider>(context);
return Scaffold(
backgroundColor: Colors.grey.shade300,
body: SafeArea(
child: SingleChildScrollView(
child: Column(
children: [
Text(proPersonaje.leerLvLPersonaje.toString()),
],
),
),
),
);
}
}
SharedPreferences.dart
class SharedPrefsPersonaje {
static final SharedPrefsPersonaje _instancia = SharedPrefsPersonaje._internal();
factory SharedPrefsPersonaje() {
return _instancia;
}
SharedPrefsPersonaje._internal();
late SharedPreferences prefs;
initPrefs() async {
prefs = await SharedPreferences.getInstance();
}
int get readExpPersonaje {
return prefs.getInt("experiencia_personaje") ?? 0;
}
set saveExpPersonaje(int value) {
prefs.setInt("experiencia_personaje", value);
}
int get readLvLPersonaje {
return prefs.getInt("nivel_personaje") ?? 0;
}
set saveLvLPersonaje(int value) {
prefs.setInt("nivel_personaje", value);
}
void eliminar() {
prefs.clear();
}
}
main.dart
void main() {
inicializamosCargaDatos();
runApp(AppState());
}
class AppState extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
//Cargamos datos del JSON, servidor
ChangeNotifierProvider(
create: (_) => PersonajeProvider(),
lazy: false,
),
],
child: MyApp(),
);
}
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: "My APP",
initialRoute: "/",
getPages: [
GetPage(
name: "/",
page: () => const PersonajeHomePage(),
),
],
);
}
}
Future<void> inicializamosCargaDatos() async {
WidgetsFlutterBinding.ensureInitialized();
final prefs = SharedPrefsPersonaje();
await prefs.initPrefs();
}
Provider_personajes.dart
class PersonajeProvider with ChangeNotifier {
final prefs = SharedPrefsPersonaje();
int xpPersonaje = 0;
int lvlPersonaje = 0;
get leerXPPersonaje {
if (prefs.readExpPersonaje != 0) {
return xpPersonaje = prefs.readExpPersonaje;
}
return xpPersonaje;
}
set guardarXPPersonaje(int xp) {
xpPersonaje = xp + xpPersonaje;
prefs.saveExpPersonaje = xpPersonaje;
notifyListeners();
}
get leerLvLPersonaje {
if (prefs.readLvLPersonaje != 0) {
return lvlPersonaje = prefs.readLvLPersonaje;
}
return lvlPersonaje;
}
set guardarLvLPersonaje(int lvl) {
lvlPersonaje = lvl + lvlPersonaje;
prefs.saveLvLPersonaje = lvlPersonaje;
notifyListeners();
}
void borrarDatos() {
prefs.eliminar();
}
}
You need to do something similar to what you do when using firebase:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await inicializamosCargaDeDatos();
runApp(const MyApp());
}
This way, you're making sure that nothing in your app is executed before your instance is initialized.
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
I have the following code, to get initial data for the screen and this SchedulerBinding seems to be a hack, but if I remove it, request data is lost.
I think it happens due to the fact widgets(streamBuilders etc.) are not yet built.
Any ideas how can I fix this?
Full screen code: https://gist.github.com/Turbozanik/7bdfc69b36fea3dd38b94d8c4fcdcc84
Full bloc code: https://gist.github.com/Turbozanik/266d3517a297b1d08e7a3d7ff6ff245f
SchedulerBining is not a hack,according to docs addPostFrame call callback only once and if you remove it your stream will never get the data
but you can call your stream loading in iniState
void initState(){
super.initState();
_mblock.loadSpotMock();
}
You can load your data asynchronously in the initState method, meanwhile you can show a loader or message. Once your data has loaded, call setState to redraw the widget.
Here is an example of this:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
createState() => new MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
String _data;
Future<String> loadData() async {
// Simulate a delay loading the data
await Future<void>.delayed(const Duration(seconds: 3));
// Return the data
return "This is your data!";
}
#override
initState() {
super.initState();
// Call loadData asynchronously
loadData().then((s) {
// Data has loaded, rebuild the widget
setState(() {
_data = s;
});
});
}
#override
Widget build(BuildContext context) {
if (null == _data) {
return Text("Loading...");
}
return Text(_data);
}
}
You can test it in https://dartpad.dartlang.org
It works like this:
initState will call loadData asynchronously, then the build method will draw the widget.
when loadData returns, the call to setState will redraw the widget.
Using StreamBuilder
The following example uses a StreamBuilder to show the data, once it's loaded:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
#override
createState() => new MyWidgetState();
}
class MyWidgetState extends State<MyWidget> {
// Create a stream and execute it
final Stream<String> _myStream = (() async* {
// Simulate a delay loading the data
await Future<void>.delayed(const Duration(seconds: 3));
// Return the data
yield "This is your data!";
})();
#override
Widget build(BuildContext context) {
return StreamBuilder<String>(
stream: _myStream,
builder: (BuildContext context, s) {
String result;
if (s.hasError) {
result = "Error";
}
else {
if (s.connectionState == ConnectionState.done) {
result = s.data;
}
else {
result = "Loading...";
}
}
return Text(result);
}
);
}
}
Hope this helps :)
I am trying to get data from Database, but my widget is built before I can get them...
class CategoriesWidget extends StatefulWidget {
#override
_CategoriesWidgetState createState() => _CategoriesWidgetState();
}
class _CategoriesWidgetState extends State<CategoriesWidget> {
SharedPreferences prefs;
String token;
var _isInit = false;
#override
void initState() {
if (!_isInit) {
super.initState();
fetchCat();
_isInit = true;
}
}
var categories = {};
fetchCat() async {
final prefs = await SharedPreferences.getInstance();
setState(() {
token = prefs.getString('api_token');
});
await fetchCategories(token).then((result) {
categories = result[1];
print(categories);
print(result[1]);
});
}
#override
Widget build(BuildContext context) {
final deviceSize = MediaQuery.of(context).size;
print('2');
return Column(
// code here
);
}
}
you can see that I print 1 and 2 to see which one is getting the first and I got as result 2 then 1.
You should use a FutureBuilder.
#override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _fetchCat(),
builder: (context, snapshot) => snapshot.hasData
? MyWidget(data: snapshot.data)
: Text('Loading...'),
);
}
And with a FutureBuilder, your Widget could probably stay Stateless.
Here is a Minimal Working Example:
Full source code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'StackOverflow Answer',
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: CategoriesWidget()),
);
}
}
class CategoriesWidget extends StatefulWidget {
#override
_CategoriesWidgetState createState() => _CategoriesWidgetState();
}
class _CategoriesWidgetState extends State<CategoriesWidget> {
Future<String> _fetchCat() async {
await Future.delayed(Duration(seconds: 2));
return 'Category';
}
#override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _fetchCat(),
builder: (context, snapshot) => Text(
snapshot.hasData ? snapshot.data ?? 'NO CATEGORY' : 'Loading...'),
);
}
}
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); //
}
}