Flutter conditional statement with output of jsondecode var - flutter

I want to show different images for different outputs of JSONdecode. I get var between 01d and 50d. For example, when it gives out 04d, I want to show Image 'assets/night.png' and for 05d, I want to show Image 'assets/afternoon.png' and more. I am a complete beginner with flutter, this is what I thought about:
var current_icon;
Future getWeather () async {
http.Response response = await http.get(Uri.parse("https://api.openweathermap.org/data/2.5/weather?q=city&appid=****"));
var results = jsonDecode(response.body);
setState(() {
this.current_icon = results['weather'][0]['icon'];
});
}
#override
void initState () {
super.initState();
this.getWeather();
}
and then put it in my container here:
new Container(
height: double.infinity,
width: double.infinity,
child: new Image(
image: (What should I do here?),
fit: BoxFit.cover,
),
),

As per your query assuming all images comes from assets. You can make cases as per your requirement like this:
String getImageData(String id) {
String value = '';
switch (id) {
case '01d':
value = 'assets/a.png';
break;
case '02d':
value = 'assets/b.png';
break;
default:
value = 'assets/c.png';
}
return value;
}
pass below widget as child of your container widget
Widget imageWidget() {
return Image.asset(
getImageData('01d'), // pass your value here
fit: BoxFit.cover,
);
}

Related

How to uninitialize a variable In Flutter / Dart?

I define a variable ui.Image? previewImage; (ui refers to dart:ui).
I then start an image stream from the camera that performs YOLOX classifications and pairs them with an image, and updates the variable previewImage with the classification results with a function onDetected():
onDetected(
List<YoloxResults> results,
ui.Image image,
) {
setState(() {
yoloxResults = results;
previewImage = image;
});
}
The yoloxResults and previewImage are then displayed with a widget:
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (previewImage == null) {
return const Center(
child: CircularProgressIndicator(),
);
}
final cameraWidget = FittedBox(
child: SizedBox(
width: previewImage!.width.toDouble(),
height: previewImage!.height.toDouble(),
child: CustomPaint(
painter: YoloxResultsPainter(
image: previewImage!,
results: results,
labels: labels,
),
),
),
);
return cameraWidget;
},
);
}
I have a page where I switch between the above widget and another body widget via a Ternary Operator...
The first time I load the cameraWidget, previewImage is null and displays the CircularProgressIndicator() before the previewImage is set via the onDetected() function (which is what I want).
The second time I open the cameraWidget previewImage is already defined and an old image is displayed before the onDetected() function updates... I have tried setState previewImage = null, which calls the CircleProgressIndicator() but then displays an old previewImage first, and before updating...
My Question: How can I reset the previewImage variable to being uninitialized?
How can I remove the data assigned to previewImage?

Getting location data in initState

I need to get location data which is used to calculate the distance between the user and other locations. This is on the app's home page and I don't want to do this every time the page loads, that's why I set up a timestamp and the location is grabbed only if five minutes pass.
What I have now is something like this in the home page:
LocationData _currentPosition;
#override
void initState() {
super.initState();
_getLocationData();
}
_getLocationData() async {
final content = Provider.of<Content>(context.read());
final _timestamp = DateTime.now().millisecondsSinceEpoch;
final _contentTimestamp = content.homeTimestamp;
if ((_contentTimestamp == null) ||
((_timestamp) - _contentTimestamp) >= 300000) {
try {
_locationData = await location.getLocation();
content.homeTimestamp = _timestamp;
setState(() {
_currentPosition = _locationData;
});
} on Exception catch (exception) {
print(exception);
} catch (error) {
print(error);
}
}
}
And I store the timestamp in Provider because I want it to persist when the user leaves the home page and returns. Not sure how to set it up without notifyListeners()
int _homeTimestamp;
int get homeTimestamp => _homeTimestamp;
set homeTimestamp(int newValue) {
_homeTimestamp = newValue;
notifyListeners();
}
My problem is that sometimes the location doesn't get stored and the page doesn't load. Is there a better way to do this?
I was thinking of adding a FutureBuilder in the body, but that would mean that the location will be retrieved every time the user loads the page, or I can just do the timestamp check in the body and not load the FutureBuilder all the time, but that doesn't seem right.
body: _currentPosition == null
? Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 20.0,
width: 20.0,
child: CircularProgressIndicator(),
),
SizedBox(
width: 10.0,
),
Text("Getting your location..."),
],
),
)
: Column(
...
use shared preference to check if the _getLocationData() method is called and use Timer.periodic(); method to call the function every five minutes.

Accesing screen width outside of context

I am creating a class for notifications that is outside of context using bot_toast, which will be called on API errors and such. I need to leave a margin that is relative to the screen width, but I don't have the context here. I could pass the context around, it doesn't seem like the best option.
So I am ending up with something like this
Function CustomToast (message) {
return BotToast.showAttachedWidget(
attachedBuilder: (_) => Align(
alignment: Alignment.topRight,
child: Container(
margin: new EdgeInsets.only(top: 25, right: 25.0),
color: Colors.blue,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
message,
),
),
),
),
duration: Duration(seconds: 20),
target: Offset(520, 520));
}
I need to set the righ margin as a percentage. How would I access the screen width here? is it possible without context? Any suggestion is welcome, even a change of toast library
Perhaps an app level model. Once you have a context, you can set the screen resolution for later retrieval of calculated percentage. I use this approach frequently.
class ScreenInfoViewModel {
List<String> _setupCompleted = [];
String appName;
String packageName;
String version;
String buildNumber;
double screenWidth;
ScreenInfoViewModel() {
init();
}
init() async {
var packageInfo = await PackageInfo.fromPlatform();
appName = packageInfo.appName;
packageName = packageInfo.packageName;
version = packageInfo.version;
buildNumber = packageInfo.buildNumber;
}
bool _smallScreen = false;
bool _mediumScreen = false;
bool _largeScreen = false;
void setScreenSize(double this.screenWidth, double diagonalInches) {
_smallScreen = diagonalInches < 5.11;
_mediumScreen = !_smallScreen && diagonalInches <= 5.6;
_largeScreen = diagonalInches > 5.6;
}
bool get isSmallScreen => _smallScreen;
bool get isMediumScreen => _mediumScreen;
bool get isLargeScreen => _largeScreen;
String get screenSize {
String size = 'S';
if (_mediumScreen) size = 'M';
if (_largeScreen) size = 'L';
return size;
}
}
I use it this way:
class SetupScreenInfo extends HookWidget {
// Use GetIt package to retrieve the model
final ScreenInfoViewModel _s = locator();
final _scaffoldKey = new GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
/// This is the first 'context' with a MediaQuery, therefore,
/// this is the first opportunity to set these values.
/// widthPx and diagonalInches are from sized_context package.
_s.setScreenSize(context.widthPx, context.diagonalInches);
WidgetsBinding.instance.addPostFrameCallback((_) {
if (context == null) return;
Navigator.pushReplacementNamed(context, splashRoute);
});
return SafeArea(
child: Scaffold(
key: _scaffoldKey,
body: Material(color: Colors.yellow[300]),
),
);
}
}
Finally, SetupScreenInfo is my initial route from my MaterialApp.
This code is an edit of my production code and has not been tested and is not a runnable example.
Hope this help generate some thought.

ImagePicker, how to set image again?

Minimal Code:
File _file;
Future<void> _pickImage() async {
final image = await ImagePicker.pickImage(source: ImageSource.camera);
if (image != null) {
final file = File("${(await getApplicationDocumentsDirectory()).path}/image.png");
await file.writeAsBytes(await image.readAsBytes());
setState(() => _file = file); // `_file = image` works though
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(child: Icon(Icons.camera_alt), onPressed: _pickImage),
body: _file == null ? Container() : Image.file(_file),
);
}
Watch video
As you can see, once I pick the image, it works, but on picking it second time, it doesn't work and I also don't run into any error. Can anyone please help?
you need 3 things:
first you have to use ImageProvider and its evict() method:
var image = FileImage(File('someImage.jpg'));
then you need Image widget that uses above ImageProvider and also assigns a unique key in order to be "different" each time build() method is called:
child: Image(
image: image,
key: UniqueKey(),
),
and finally after you overwrite someImage.jpg you have to call evict() method:
// part of your _pickImage() method
// here someImage.jpg contains updated content
image.evict();
setState(() {});
UPDATE: actually you dont need var image = FileImage(File('someImage.jpg')); - you can use it directly inside Image widget as image: FileImage(File('someImage.jpg')) and call FileImage(File('someImage.jpg')).evict() after your image is ovewritten

How to solve the index in list view is not correct

I am using list view + pagination in Flutter to show my response data.
I face the problem when I selected the first list item the details such as id is the other list item id. I have used print() to check the id, it always shows the wrong id.
I want to show the details about my image, but it gives me wrong id. So it will show other image.
How can I solve the problem?
There is no need to define id and title as variables of the State object.
You can pass them as a parameter to the selectedItem method instead, the problem is you always set the id and title to the last item built so it will always navigate with its details instead of the actually selected item.
class _HomePage State extends State<HomePage > {
GlobalKey<PaginatorState> paginatorGlobalKey = GlobalKey();
#override
Widget build(BuildContext context) {
body: return Paginator.listView(
key: paginatorGlobalKey,
pageLoadFuture: sendPagesDataRequest,
pageItemsGetter: listItemsGetterPages,
listItemBuilder: listItemBuilder,
loadingWidgetBuilder: loadingWidgetMaker,
errorWidgetBuilder: errorWidgetMaker,
emptyListWidgetBuilder: emptyListWidgetMaker,
totalItemsGetter: totalPagesGetter,
pageErrorChecker: pageErrorChecker,
scrollPhysics: BouncingScrollPhysics(),
);
}
Future<PagesData> sendPagesDataRequest(int page) async {
String url = Uri.encodeFull("https://API_URL?page=$page");
http.Response response = await http.get(url);
PagesData pagesData = pagesDataFromJson(response.body);
return pagesData;
List<dynamic> listItemsGetterPages(PagesData pagesData) {
List<Project> list = [];
pagesData.data.forEach((value) {
list.add(value);
});
return list;
}
Widget listItemBuilder(dynamic item, int index) {
return InkWell(
onTap: () => selectedItem(item,context), // pass the item iteself in the selectedItem function
child: new CachedNetworkImage(
imageUrl:= item.image,
placeholder: (context, url) => new CircularProgressIndicator(),
errorWidget: (context, url, error) => new Icon(Icons.error),
fit:BoxFit.fill,
),
);
}
Widget loadingWidgetMaker() {
return Container(
alignment: Alignment.center,
height: 160.0,
child: CircularProgressIndicator(),
);
}
void selectedItem(dynamic item,BuildContext context) { // add a parameter for item
Navigator.of(context).pushNamed(
DetailsPage.routeName,
arguments: {
'id': item.id, // Move the item.id here
'title': item.title // Move the item.title here
});
}
}