Load List of images consequtively. Flutter - android

I am building a comics app. In order to make comic chapters load faster. I want images to load one by one consequtively. When an image finishes loading, it is presented directly into a pageview. I do not know how to load images one by one and edit the page view to present them.

In order to make load image faster you can try use cached_network_image package like this:
CachedNetworkImage(
imageUrl: "http://via.placeholder.com/350x150",
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
),
It will show placeholder until the image loaded, so the user can see how many image there is and wait until it loaded, and errorWidget to show if some image were getting trouble. So I suggest to use it, it's easier. Unless you want some complicated code you can try use ScrollController put it inside your ListView.builder. Initialize your scroll controller in initState like this :
final _scrollController = ScrollController();
#override
void initState() {
super.initState();
_scrollController.addListener(() {
if (_scrollController.position.pixels ==
_scrollController.position.maxScrollExtent) {
// scroll has reach end, now load more images.
loadMore();
}
});
}
You can't use setState inside initState so that's why I create another method to call it like this:
Future<void> loadMore() async {
final response = await api.get("curated?per_page=50&page=$_currentPage");
if (response.statusCode == 200) {
var tempList = Pages.fromJson(response.data);
setState(() {
isLoading = false;
wallpaper.addAll(tempList.photos);
});
}
return null;
}
So there you go I hope you can understand, and yes it is more complicated, so like I said I suggest you use CachedNetworkImage.

use a StreamBuilder it will do all the work for u.

Related

is it possible to force convert webview text divs to buttons?

i have an app that renders a webpage inside a webview. this actually has a bunch of crypto addresses.
I want them to be automatically made clickable. and when they are clicked - i want to show a popup (some information about the addresses).
can this be done ?
im very unsure if this thing about changing the UI is possible...but in desktop web world, there are extensions that do this. if there are any examples of flutter webview codebases that do this, that would be helpful
the second point - communicating back and forth with the webpage is even more confusing. can this be done at all ? can i receive the data of the click back to main flutter app and then do something ?
Here's a working example
This is achieved as I said by Injecting Js when the webpage loads, as for communication with Flutter, I used the flutter Webview plugin provided JavascriptChannel
The Javascript code looks for a specific element firstly on Page load and secondly while scrolling the webpage (to account for newly created dynamic elements)
Here's how the flow works
JS: assigns the element a new css style (Or in your case make it look like a button) or even create a button and insert it into the webpage
JS: assign on click to the element to call the Flutter JS Channel.
Flutter: Receive message Display a snackbar - you can deeplink or do whatever you want.
As the comments on the JS code say. the scrolling behavior calls every time which is not always ideal, you can use another function make it only trigger on a specific scroll distance
Full working example
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
dynamic ctrl;
// if you have multiple elements, just use querySelector All and loop
const jsCodeToForAnElement = """
// Choose the element
watch();
// I would add dealy to calculate delta between scrolls
// Meaning this code wont watch on every scroll
window.onscroll = watch();
function watch(){
let elm = document.querySelector("main h1");
try{
// Style it
elm.style.cssText = "background: red";
// Add on click
elm.onclick = (event)=> {
var walletHTMLElement = event.target;
// Use native API to communicate with Flutter
jsMessager.postMessage('Wallet clicked: ' + walletHTMLElement.innerHTML);
};
}catch(e){
jsMessager.postMessage('Error: ' + e);
}
}
""";
void main() {
runApp(
MaterialApp(
home: WebViewExample(),
),
);
}
class WebViewExample extends StatefulWidget {
#override
WebViewExampleState createState() => WebViewExampleState();
}
class WebViewExampleState extends State<WebViewExample> {
#override
void initState() {
super.initState();
// Enable virtual display.
if (Platform.isAndroid) WebView.platform = AndroidWebView();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: WebView(
initialUrl: 'https://flutter.dev/',
debuggingEnabled: true,
onWebViewCreated: (WebViewController webViewController) {
ctrl = webViewController;
},
javascriptMode: JavascriptMode.unrestricted,
javascriptChannels: <JavascriptChannel>{
JavascriptChannel(
name: 'jsMessager',
onMessageReceived: (jsMessager) async {
if (jsMessager.message.contains("Wallet")) {
var snackBar = SnackBar(
content: Text(jsMessager.message),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
// Do Other Thing like deeplinking to crypto app
// Deeplink to wallet LaunchApp(wallet) <-- clean wallet string first
}
}),
},
onPageStarted: (String url) async {
ctrl.runJavascript(jsCodeToForAnElement);
},
onPageFinished: (String url) async {},
),
);
}
}
Original Answer
This would be possible by Injecting Javascript into Webviews (This is one idea)
1 - I would wait for the page to load
2 - Modify the HTML content using Javascript
Should be pretty straight forward.
Refer to this Answer to see how it is done in Flutter.
https://stackoverflow.com/a/73240357/6151564

Flutter - How to use mounted in GetX

I initiate network request in GetXController, after network call back, I should judge this controller/this page is dealloc or not. If this page is not dealloced, update Page. If this page is dealloced, I do noting. As I know, I can write below codes in flutter origin:
if (mounted) {
// update page
setState({
});
}
So my question is how to write in GetX controller?
There is a property called isClosed in GetxController
so you can use it instead of mounted
class MyController extends GetxController{
...
fun() {
// some code
if(this.isClosed) return;
// code that you want not execute it
}
...
}
mounted can only be called inside Stateful widgets, so you can't use it inside a Controller.
If you are using named routes I think you can get the current name of the page and do something.
if(Get.routing.current == "/home"){
doSomething();
}
the mounted bool is specific only for the StateFulWidget, I could think of passing it as a Stream<bool>to the controller, then use it, But, this is not the ideal solution and it can be very problematic.
On the other hand, you can check on mounted before calling the method, like this:
// ....
onPressed: () {
if (mounted) {
controller.sendRequest();
}
},
// ....

How can I filter the content of listview when clicked a button?

I'm not using API or firebase yet, the data is stored locally.
here is my code
======>>
Widget _buildhouse(BuildContext contex, int index){
Size size = MediaQuery.of(context).size;
House house = houselist[index]; //houselist is the list of all houses
return GestureDetector(
onTap: (){
setState(() {]
house = filteredhouse[index]; //this code wont be executed
print(house.price);
});
Navigator.push(context, MaterialPageRoute(builder: (_) => DetailsScreen(house),));
},
so those two lines that I commented on are the important ones I guess, the print code gets executed but not the other one. also if I say "house = filteredhouse[index];" at the beginning, I will get the filtered value. but it won't get changed when clicked the button
solved it by using this function void _filtered(){ setState(() { houselist = filteredhouse; }); } and then calling it inside of setState

How to read and use shared preference values in initstate()? I can read and use values in other widgets but not in my API which i call in initstate

I am creating a list of businesses through RestAPI in initstate. I need to use sharedpreference value as one of its parameter but when i try to load data, the sharepreference value is null initial but that value can be used in other widgets easily.
The problem is how to read sharepreference value and use at the same time in initstate.?
I have tried many options like taking my api out of initstate and defining with async function and call it in initstate.
**#override
void initState() {
super.initState();
loadData();
getBizList();
}
getBizList() async {
await api
.bizListAPI(widget.subCatid, savedCityID, supSubFinal, areaFinal)
.then((response) {
setState(() {
bizListOBJ = response;
});
});
}
loadData() async {
SharedPreferences savedPref = await SharedPreferences.getInstance();
setState(() {
savedCity = (savedPref.getString('savedCity') ?? "Mumbai");
savedCityID = (savedPref.getString('savedCityID') ?? "1");
});
}**
in above code i am able to get or read data and show city name from sharedpreference(savedCity) which is in appbar but i want to use savedcityID data to pass in my api in initstate.
I also tried to use following plugin
[https://medium.com/#greg.perry/a-flutter-class-for-app-preferences-a256166ecc63.][1]
With this I was able to do what i wanted but each time i close my app and open again ...
i got following error
"_prefsInstance != null,
"Maybe call Prefs.getKeysF() instead. SharedPreferences not ready yet!");"
after that when i go back and open page again, the app and biz list works perfect till again i close my app.
[1]: https://medium.com/#greg.perry/a-flutter-class-for-app-preferences-a256166ecc63
sorry for my any noob explanation or question. Actually this is my 1st question in stack and I am beginner in flutter.
Can you try this?
void initState() {
super.initState();
loadData().then((_) {
getBizList();
});
}
You need to use set state inside init state because
loadData();
getBizList();
are async function so you need to refresh page to get value.
Try this
#override
void initState() {
// TODO: implement initState
super.initState();
setState(() {
loadData();
getBizList();
});
}

Refresh view after Pop with react-native-router-flux

I have big problem with my first React Native app. I've spend few hours today to find the solution but unfortunately nothing helps me.
I have two views:
<Router>
<Stack>
<Scene key="objectsList"
component={ ObjectsList } title="Objects"
onRight={() => Actions.addObject()} rightTitle="Add object" />
<Scene key="addObject" component={ AddObject } title="Add" />
</Stack>
</Router>
First view is a FlatList that is displaying data loaded from AsyncStorage. User can add new object by pressing the right button Add object on the navigation bar. AddObject Scene will be present on the screen. There is another FlatList with some options. User can tap on the cell and then the data is save to the AsyncStorage and after that I call Actions.pop(); function.
The view is gone but I couldn't refresh first screen Objects to reload data and display new value. I've tried a lot solutions from git, stackoverflow and other forums. Nothing works for me.
I am using react-native-router-flux for routing.
I will be glad for help or example code because I am stuck on this problem and it block my project.
P.S. I want to refresh view, send new props or do something that will not reload whole Component. There is a map on it and if I am reloading whole View it is loading from the beginning. I want only to inform that there is some change and run some function to refresh it.
EDIT:
After implemented the solution proposed by Phyo unfortunately I couldn't refresh the data in proper way. When user choose something on AddObject scene he comes back to first view but nothing happened. The change is implemented after user open AddObject scene second time. The function from props is running only when scene 2 is appear again.
My second attempt looks like that:
ObjectsList
state = { selectedObject: "" }
componentDidMount() {
this.loadSelectedObject();
}
refreshState() {
this.loadSelectedObject();
}
loadSelectedObject() {
AsyncStorage.getItem('ObjectKey', (err, object) => {
if (object) {
this.setState({ selectedObject: object })
}
});
}
render() {
return(
<Button onPress={() => Actions.addObject({onBack: refreshState()})}>
);
}
Add Object
onPressItem = (object) => {
AsyncStorage.setItem('ObjectKey', object, () => {
this.props.onBack;
Actions.pop();
});
};
render() {
return (
<FlatList
data={this.state.objects}
keyExtractor={(item, index) => item}
key={this.state.numColumns}
numColumns={this.state.numColumns}
renderItem={(data) =>
<LineCell onPressItem={this.onPressItem} object={String(data.item.object)} />
);
}
I found a solution with friend's help. The code:
ObjectsList
state = { selectedObject: "" }
componentDidMount() {
this.loadSelectedObject();
}
refreshState() {
this.loadSelectedObject();
}
loadSelectedObject() {
AsyncStorage.getItem('ObjectKey', (err, object) => {
if (object) {
this.setState({ selectedObject: object })
}
});
}
render() {
return(
<Button onPress={() => Actions.addObject()}>
);
}
Add Object
onPressItem = (object) => {
AsyncStorage.setItem('ObjectKey', object, () => {
Actions.pop();
Actions.refs.objectsList.refreshState(); //Solution
});
};
On your first view, create a function e.g., refreshFirstView() and pass that function to the second view.
On your second view, call that function this.props.refreshFristView() before you called Actions.pop()
In your refreshFirstView() on the first view, simply call all the function you called to retrieve from AsyncStorage.
First View
componentDidMount(){
retrieveAsynStorageItems()
doSomeOtherLikeFilteringOrSorting()
}
refreshFirstView(){
retrieveAsynStorageItems()//by calling this func again you will get newly saved item
doSomeOtherLikeFilteringOrSorting()
}
I hope this is what you need.

Categories

Resources