So a piece of code I wrote needs to run on both web and mobile. And since some libraries like dart: io on the web and can't use dart:html on mobile. So in order to pick images on the web, I tried to use ImagePicker which returns an X-File, turns the X-File to bytes, and uses Image. memory().
This is the piece of code that I've been trying:
class _CanvasImageItemState extends State<CanvasImageItem> {
Uint8List? imageBytes;
XFile? file;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
// widgets
}
Widget buildPicker(BuildContext context) {
if (kDebugMode) {
print("picker loaded");
}
return InkWell(
onTap: () async {
FocusedItemModel.of(context).focus(widget.item as LocalImageItem);
if (!kIsWeb) {
// Some piece of code for mobile
} else {
await _pickImage();
}
},
child: ..some other widgets
);
}
}
_pickImage() async {
final ImagePicker _picker = ImagePicker();
file = await _picker.pickImage(source: ImageSource.gallery);
Uint8List? bytes = await file!.readAsBytes();
if (mounted) {
setState(() {});
imageBytes = bytes;
} else {
imageBytes = bytes;
}
if (kDebugMode) {
print("Checking File: ${file!.path}");
print("Checking imageBytes: ${imageBytes!.length}");
}
}
The problem is although I did not dispose() the widget or used dispose() anywhere in the project itself but I am getting
setState() called after dispose() error. So I tried checking if(mounted) before trying to update the value of imageBytes but in that case, it's taking two tries to pick the image.
As you can see I even tried to print the path of xFile and imageBytes which is getting printed properly. But I cannot set the state.
N.B: The CanvasImageItem is child of another widget called CanvasMovableItem. Any suggestion where the problem is occurring from? I've checked all possible codes that might unmount or dispose of the widget.
Am I missing something very simple?
Related
I am coding a Pokedex app with API, with simple http get, my StateFul Widget looks like this:
class _PokemonGridState extends State<PokemonGrid> {
List? pokedex;
Future<void> searchPokemonGOData() async {
var url = Uri.http(
'raw.githubusercontent.com',
'/Biuni/PokemonGO-Pokedex/master/pokedex.json',
);
await http.get(url).then(
(value) {
if (value.statusCode == 200) {
var decodeJsonData = jsonDecode(value.body);
pokedex = decodeJsonData['pokemon'];
setState(() {});
}
},
);
}
#override
void initState() {
super.initState();
if (mounted) {
searchPokemonGOData();
}
}
I want to use the function searchPokemonGOData() from MobX controller but I don't know how, the store file and .g file are already ready. I want something to replace the initState() from StateFul, is that possible?
You can see the strange behaviour in this video: https://streamable.com/r5ld2y
The InitValue is the correct one, but when I restart the App it first goes to zero, AFTER loading the Screen OR press a button, it loads the shared prefs...
This is my Cubit (Only with the LoadCounter Func):
class DrinkCubit extends Cubit<DrinkState> {
DrinkCubit() : super(DrinkState(drinkValue: 0));
Future<void> loadCounter() async {
final prefs = await SharedPreferences.getInstance();
state.drinkValue = (prefs.getInt('counter') ?? 0);
}
}
And this is my InitFunction in the main window!
#override
void initState() {
super.initState();
Future.delayed(Duration.zero,()
{
BlocProvider.of<DrinkCubit>(context).loadCounter();
});
}
So how do I fix this, that the correct value is directly after starting the app showed
Try this:
getData(){
BlocProvider.of<DrinkCubit>(context).loadCounter();
}
#override
void initState() {
SchedulerBinding.instance.addPostFrameCallback((_) {
getData();
});
super.initState();
}
SchedulerBinding.instance.addPostFrameCallback ensures that code inside run before UI code.
And if it allows me to give you a hint, is better remove SharedPreferences of your Bloc and put in another class.
I'm building a Flutter app, and have a page with a table that is populated with data. I load the data like so:
class _AccountMenuState extends State<AccountMenu> { {
List<Account> accounts;
Future<List<Account>> getAccounts() async {
final response = await http.get('http://localhost:5000/accounts/' + globals.userId);
return jsonDecode(response);
}
setAccounts() async {
accounts = await getAccounts();
}
#override
void initState() {
setAccounts();
super.initState();
}
}
This works as expected when hot reloading the page, but when I route to this page via MaterialPageRoute,
like so: Navigator.push(context, MaterialPageRoute(builder: (context) => AccountMenu()));
then the data is not there.
What am I missing? I thought initState() gets called whenever a page loads?
You cannot do setState inside initState directly but you can wrap the initialization inside a PostFrameCallback to make sure that the initState lifecycle of the Widget is done.
class _AccountMenuState extends State<AccountMenu> { {
List<Account> accounts;
Future<List<Account>> getAccounts() async {
final response = await http.get('http://localhost:5000/accounts/' + globals.userId);
return jsonDecode(response);
}
setAccounts() async {
accounts = await getAccounts();
setState(() {})
}
#override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) => setAccounts());
super.initState();
}
}
initState() will not wait for setAccounts() to finish execution. In the method setAccounts() call setState after loading data.
setAccounts() async {
accounts = await getAccounts();
setState((){});
}
initState does not await. It only loads functions before the widget builder but it does not await.
you need to await loading widgets with data until accounts.length is not empty.
Show loading widget while data still loads or use FutureBuilder
List<Account> accounts;
#override
void initState() {
setAccounts();
super.initState();
}
#override
Widget build(BuildContext context) {
accounts.length > 0 ? SHOW_DATA_HERE : LOADING_WIDGET_HERE
}
I have a flutter camera app and am able to get a recorded video to play. The problem is, I am only able to get it to play when a button is pressed. How do I get the code to run when the widget(screen) is created instead of when the button is pressed so I don't have to press a button to get it to play? Here is my code:
Here is the code for when the button is pressed:
//raised button
RaisedButton(
onPressed: () {stopButtonPressed();},
//stopButtonPressed
void stopButtonPressed() {
print('stopButtonPressed hit');
stopVideoRecording().then((_) {
print('StopVideoRecording complete');
});
}
//stopVideoRecording
Future<void> stopVideoRecording() async {
print('stopVideoRecording hit');
await _startVideoPlayer();
}
//_startVideoPlayer
Future<void> _startVideoPlayer() async {
print('_startVideoPlayer hit');
print(Provider.of<SendDataModel>(context, listen: false).displayImageVideo());
final VideoPlayerController vcontroller =
VideoPlayerController.file(File(Provider.of<SendDataModel>(context, listen: false).displayImageVideo()));
videoPlayerListener = () {
if (videoController != null && videoController.value.size != null) {
if (mounted) setState(() {});
videoController.removeListener(videoPlayerListener);
}
};
vcontroller.addListener(videoPlayerListener);
await vcontroller.setLooping(true);
await vcontroller.initialize();
await videoController?.dispose();
if (mounted) {
setState(() {
//saveImagePath = null;
videoController = vcontroller;
});
}
await vcontroller.play();
} //startVideoPlayer
Thanks!
You can call the function from initState(). initState() is called only once when the StatefulWidget is inserted into the Widget tree, so it's a good place to initialize variables or do what you're trying to do.
class _MyHomePageState extends State<MyHomePage> {
#override
void initState() {
super.initState();
// Do anything you need done here
_startVideoPlayer();
// If you want a slight delay, use Future.delayed
Future.delayed(Duration(seconds: 1), (){
_startVideoPlayer();
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
// rest of app
This ShakePlugin is not working with this piece of code,when im just using this code without these api calls and all its working fine.
class MyAppState extends State<MyApp> {
List data;
String _search = 'nature';
int index = 0;
File imageFile;
String imageData;
bool dataLoaded;
var path;
int count = 10;
FlutterShakePlugin _shakePlugin;
void initState() {
super.initState();
_shakePlugin = FlutterShakePlugin(
onPhoneShaken: () {
setState(() {
count=count+10;
});
},
},
)..startListening();
}
void dispose() {
super.dispose();
_shakePlugin.stopListening();
}
Future<String> getjsondata() async {
try {
var response = await http.get(
'https://api.unsplash.com/search/photos?per_page=${count}&client_id=TcAQEO3JoMG90U7Rl-YUiDo1x9XbZukzMOMQhxUVCV4&query=${_search}');
setState(() {
var converted = json.decode(response.body);
data = converted['results'];
});
} catch (e) {}
return 'success';
}
void saveImage(int i) async {
var url = data[i]['urls']['small'].toString();
var imageId = await ImageDownloader.downloadImage(url);
path = await ImageDownloader.findPath(imageId);
}
#override
Widget build(BuildContext context) {
getjsondata();
return GestureDetector(
child: SwipeDetector(
child: Container(
child: Image.network(
data[index]['urls']['small'],
I want to increase the count of images i recieve from api on shake of screen but this is not working even if i have installed all the libraries and all.
Calling your getjsondata method in the build method will cause the ui to render infinitely because you're calling setState in getjsondata. I think the shake plugin is working fine but its result is void because the screen is in an infinite render state.
If you move getjsondata to a FutureBuilder, remove the setState call from inside the getjsondata method and render your ui on the result of the Future your code should work.