I am working on app to import a lit from google drive in the form of JSON file, the App will read the JSON file only, the reason is I am not using Firestore Database is because it delays the build of the App & whatever I tried still I have errors & I can't build the APP on IOS device or simulator, so every time I will try to import the data the App will show error. my code is as the following
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Samer'),
),
body: FutureBuilder<List<User>>(
future: UsersApi.getApps(),
builder: (context, snapshot) {
final users = snapshot.data;
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return const Center(child: Text('error'));
} else {
return buildUsers(users!);
}
}
},
),
);
}
Widget buildUsers(List<User> users) => ListView.builder(
physics: BouncingScrollPhysics(),
itemCount: users.length,
itemBuilder: (BuildContext context, int index) {
final user = users[index];
return Padding(
padding: const EdgeInsets.all(15.0),
child: Container(
color: kOurAppsMainScreenCards,
child: ListTile(
title: Text(user.appName),
// leading: CircleAvatar(
// backgroundImage: NetworkImage(user.imageName),
// ),
// subtitle: Platform.isIOS
// ? Text(user.paidFreeIOS)
// : Text(user.paidFree),
),
),
);
},
);
}
and I am using http package as following:
class UsersApi {
static Future<List<User>> getApps() async {
final url = Uri.parse(
'https://drive.google.com/file/d/1tAxO2kRD0NVBhefA3srbj1SKQ2l8u9Wc/view?usp=sharing');
final response = await http.get(url);
final body = json.decode(response.body);
return body.map<User>(User.fromJson).toList();
}
}
the stage thing is I inserted the same JSON file in firestore storage and the App read it....
Can somebody please help me.
Regards,
I finally found a solution to this, the main issue is in the link itself, we have to do some modification to the link & then we can use it freely, for example:
if this is the file link, we will use the bold lines in the modified link
https://drive.google.com/file/d/1-T9OTLTZXTB7ydqV3tcgG4T2FQckMooB/view?usp=sharing
and we have to add few words to make the link workable 100%,
the following code to be added
uc?export=view&id=
the new link will be
https://drive.google.com/uc?export=view&id=1-T9OTLTZXTB7ydqV3tcgG4T2FQckMooB
Solution ref (it was for photos but it worked in JSON files also)
https://youtu.be/0ZHqrf0mzrI
Related
I'm using the settings_ui package.
I'm making an app that primary will be used on Android but also iOS. In the settings screen I want to display the web view version.
My settings are made using the settings_ui package, and the app consists of a web view, using this package.
A method exists to return the web view version Android is using. To retrieve this, we need to make use of await.
I want to dynamically add a section to my list of settings if the device is Android. That part is simple.
The problem is I can't work out how to read the async value inside what I think I need, a FutureBuilder.
The settings_ui expects an array of List sections.
For example:
return SettingsList(
sections: [
SettingsSection(
title: const Text('Common'),
tiles: [
SettingsTile(
title: const Text('Web address'),
description: const Text('Change the web address.'),
leading: const Icon(Icons.language),
onPressed: (context) {
Navigator.push(
context,
MaterialPageRoute(fullscreenDialog: true, builder: (context) => const WebAddressForm()),
);
},
),...
I then have code if the device is Android (simplified for demonstration purposes):
if (showAndroidSection)
FutureBuilder<SettingsSection>(
future: _someFutureToGetAndroidWebViewVersion(), builder: (context, snapshot) => SettingsSection(tiles: []))
Doing this gives me the error "The element type 'FutureBuilder' can't be assigned to the list type 'AbstractSettingsSection'". I have tried casting to no avail.
I am aware I shouldn't be calling the future in the build method - and I will move to initState but the problem will still remain. How do I return the web view version within a dynamic SettingsSection?
I hope that makes sense. Thanks.
The way I'm working around this currently is the following, but feels wrong to me...
late String _versionName = "";
#override
void initState() {
super.initState();
enableCustomChromeTabs = SharedPrefs.instance.getBool(Settings.customChromeTabs) ?? false;
if (!kIsWeb && Platform.isAndroid) {
showAndroidSection = true;
getPackageInfo().then((value) {
setState(() {
_versionName = value!.versionName ?? "Unable to retrieve.";
});
});
}
Then later...
SettingsSection(
title: const Text(_versionName),
tiles: [
SettingsTile.switchTile(
title: const Text('xyz'),
...
I solved it as follows (unsure if this is the correct way or an "easier" way):
Added this to the array of sections:
MyWidget()
and...
class MyWidget extends AbstractSettingsSection {
#override
Widget build(context) {
return FutureBuilder<AndroidWebViewPackageInfo?>(
future: getPackageInfo(),
builder: (context, AsyncSnapshot<AndroidWebViewPackageInfo?> snapshot) {
return SettingsSection(
title: const Text("Android debugging"),
tiles: [
CustomSettingsTile(
child: Container(
padding: const EdgeInsetsDirectional.only(
start: 25,
),
child: Text(
snapshot.data,
),
),
),
],
);
});
}
}
I am attempting to fetch a list of tweets using Flutter's HTTP library and Twitter's API endpoint, and then display the list of tweets onto a ListView widget using a Flutter tweet_ui library. I was able to get the fetching of a list of tweets working correctly and I get back a JSON response. However, I am currently attempting to decode the JSON response.body and save it onto a List to later encode once I pass it to the tweet_ui method I am using. Using the code I've shown below I continue get this red error screen with the error saying "Expected a value of type 'String', but got one of type '_Future < dynamic >'". I've tried multiple times playing around with the different types that are being passed around from function to function and that does not seem to work. I've also attempted to wrap the widget around a future builder, and that did not seem to work as well. Any idea what I could possibly be doing wrong?
Future getTweetJson() async {
Map<String, String> params = {
'exclude': 'retweets',
'expansions':
'attachments.poll_ids,attachments.media_keys,author_id,entities.mentions.username,geo.place_id,in_reply_to_user_id,referenced_tweets.id,referenced_tweets.id.author_id',
'tweet.fields':
'attachments,author_id,context_annotations,conversation_id,created_at,entities,geo,id,in_reply_to_user_id,lang,possibly_sensitive,public_metrics,reply_settings,source,text',
'user.fields':
'created_at,description,entities,id,location,name,pinned_tweet_id,profile_image_url,protected,public_metrics,url,username,verified',
'place.fields':
'contained_within,country,country_code,full_name,geo,id,name,place_type',
'media.fields':
'duration_ms,height,media_key,preview_image_url,type,url,width,public_metrics,non_public_metrics,organic_metrics,promoted_metrics'
};
var response = await http.get(
Uri.https('api.twitter.com', '2/users/IDnumber/tweets', params),
headers: {
"Authorization":
"Bearer bearerToken"
});
String jsonData = response.body;
return jsonData;
}
Function used to fetch tweets using HTTP library from Twitter API
List getListOfTweets(var jsonData) {
List<dynamic> tweets = [];
tweets = convert.jsonDecode(jsonData);
return tweets;
}
Function used to hopefully convert each tweet fetched in the Json to be added to the list.
List<dynamic> tweets = handler.getListOfTweets(handler.getTweetJson());
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Tweets")),
body: Column(children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: tweets.length,
itemBuilder: (context, index) {
return Container(
child: Column(
children: [
EmbeddedTweetView.fromTweet(
Tweet.fromRawJson(
convert.jsonEncode(tweets[index]),
),
)
],
));
}),
),
]),
);
}
Widget using ListView
var tweetJson = handler.getTweetJson();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Tweets")),
body: Container(
child: Card(
child: FutureBuilder(
future: tweetJson,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: const Center(
child: Text('Loading. . .'),
),
);
} else {
var tweets =
List<String>.from(convert.jsonDecode(snapshot.data));
return ListView.builder(
itemCount: tweets.length,
itemBuilder: (context, index) {
return Container(
child: Column(
children: [
EmbeddedTweetView.fromTweet(
Tweet.fromRawJson(
convert.jsonEncode(tweets[index]),
),
)
],
));
});
}
},
),
),
),
);
}
Alternative Widget using Future Builder
I suggest to
Create a class model for tweet
Get the json data from api then decode it to json ,after map it to the
created class and then convert it to list (A future builder returning a list
of tweets) or if you use state management (skip to step 4).
Use a state management ie. Provider or Getx
The following is if you use provider as state management
if your using provider use change notifier and save the list of tweets to a list datatype something like List<your_tweet_class> and now you can access it using Provider.of(context) with the class specified
I'm trying to implement local database support in my flutter app which is being managed using Provider, now I want to make the retrieving of data obey the state management pattern, but I've been failing to.
I've tried to make a traditional Provider to achieve this but the app got stuck in a loop of requests to the database, so after some search I found the FutureProvider, but I cant find how can I get a snapshot from the data being loaded
class _ReceiptsRouteState extends State<ReceiptsRoute> {
List<Receipt> receipts = [];
#override
Widget build(BuildContext context) {
return FutureProvider(
initialData: List(),
builder: (_){
return DBProvider().receipts().then((result) {
receipts = result;
});
},
child: Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).history),
),
body: Container(
child: ListView.builder(
itemBuilder: (context, position) {
final item = receipts[position];
return ListTile(
title: Text(item.date),
);
},
),
),
),
);
}
}
now my app is running as I want but not as how it should run, I used FutureBuilder to get the data from the database directly but I know it should come through the provider so I want to make it right
FutureProvider exposes the result of the Future returned by builder to its descendants.
As such, using the following FutureProvider:
FutureProvider<int>(
initialData: 0,
builder: (_) => Future.value(42),
child: ...
)
it is possible to obtain the current value through:
Provider.of<int>(context)
or:
Consumer<int>(
builder: (context, value, __) {
return Text(value.toString());
}
);
In my example I used the create parameter of FutureProvider to request the API, then then I used Consumer to get the results of the API.
FutureProvider(
create: (_) => peopleService.getAllSurvivor(),
child: Consumer<List<Survivor>>(builder: (context, survivors, _) {
return survivors == null
? Center(child: CircularProgressIndicator())
: ListView.builder(
itemCount: survivors.length,
itemBuilder: (context, index) {
var survivor = survivors[index];
return ListTile(
title: Text(survivor.name),
subtitle: Text(survivor.gender),
leading: Icon(Icons.perm_identity),
);
},
);
})));
I am trying to get set of encrypted data documents from firebase and display them on a list view on flutter.
I used a stream builder for obtaining data and started displaying it on the list view. But I cant perform the decryption operation on each data item as it is an async operation. What is the best way to do this?
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection(ScopedModel.of<User>(context).userId)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: new Container(
child: CircularProgressIndicator(),
));
default:
if (snapshot.data.documents.length == 0) {
return Container(
padding: EdgeInsets.all(16.0),
child: Row(
children: <Widget>[
Text('Empty',),
],
),
);
}
final docs = snapshot.data.documents;
return ScrollConfiguration(
behavior: ScrollBehavior(),
child: ListView.builder(
itemCount: len,
scrollDirection: Axis.horizontal,
itemBuilder: (context, position) {
// Where should I decrypt the below data?
// let decrypted = await myDecryptionFunction(docs[position]['myDataKey']) ;
// the above is not working
// this will show the encrypted text
return Text(docs[position]['myDataKey']);
}
....
For your situation you can use a StreamController with a helper class to hold the information.
The following is just an example but adapt it to your own needs.
// Helper classes
// adapt it to your own business case
class Notes {
String title;
String description;
Notes({this.title, this.description});
}
class NotesFromDb {
String connectionState;
bool hasError;
String error;
List<Notes> notes;
NotesFromDb({this.connectionState, this.hasError, this.error, this.notes});
}
// The Streambuilder
StreamBuilder<NotesFromDb>(
stream: sController.stream,
builder: (BuildContext context, AsyncSnapshot<NotesFromDb> snapshot) {
// Here you can check for errors
if (snapshot.data.hasError == true) {
return Container(
color: Colors.red,
child: Text(snapshot.data.error),
);
}
// Here you can check for your connection state
if (snapshot.data.connectionState == 'Loading') {
return Container(
color: Colors.yellow,
child: CircularProgressIndicator(),
);
}
// Here you can show your data
var info = snapshot.data.notes.map((doc) {
return Text(doc.title);
});
return Center(
child: Container(
color: Colors.deepOrange,
child: Column(
children: info.toList(),
),
));
})
// Here is how you can handle the decrypt data
// using a FloatingActionButton for loading the data (for example)
FloatingActionButton(
onPressed: () async { // you would need to add the async
List<Notes> theNotes; //just to hold the information
// Use this to allow to show the CircularProgressIndicator
sController.sink.add(NotesFromDb(connectionState: 'Loading'));
var snapshots = Firestore.instance.collection('notes').snapshots();
snapshots.listen((QuerySnapshot data) {
theNotes = data.documents.map((DocumentSnapshot doc) {
// Build your data
return Notes(
title: doc.data['title'],
description: doc.data['description']);
}).toList();
}, onError: (err, stack) {
// If an error happend then send the error to the stream
sController.sink
.add(NotesFromDb(hasError: true, error: err.error));
});
// Here you can to decrypt the documents with your function
var decryptDocuments = await Future.delayed(Duration(seconds: 2)); //Whatever future function
// Once you have the decrypt documents, you would need to send that to the stream.
// set the connectionState to Done, so the spinner is not showed anymore.
sController.sink.add(NotesFromDb(
hasError: false, connectionState: 'Done', notes: decryptDocuments));
},
child: Icon(Icons.arrow_forward),
)
Take the example just for illustration on how to do it.
Hope this help.
I'm right now trying to make a Quiz like application in flutter for learning this Framework.
I've implemented Firebase Firestore to manage the application data.
From this flutter plugin documentations I read about binding a CollectionReference to a ListView and it was pretty easy.
My problem is the following.
I've got some categories to display in the Home page, I want the user to be able to select which one he wants and then store this information in a List.
With this code I can display the list:
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection('Categorie').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Center(
child: Column(
children: <Widget>[
SizedBox(
child: CircularProgressIndicator(),
height: 50.0,
width: 50.0,
),
Text('Dowload delle categorie...')
],
),
);
default:
_loadListOfCategories(snapshot);
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
var nome = document['Nome'];
print('doc: $nome');
return new CategoryWidget(
id: document.documentID,
tap: onCategoryTap,
nome: document['Nome'],
count: document['Count'],
checked: false,
);
}).toList(),
);
}
},
);
}
CategoryWidget is just a simple stateless widget which act as a ListTile.
The result is the following:
Now, how can I save a List full of Category models, which one implementing a "checked/unchecked" property, and how can I keep this List updated?
I tried using "_loadListOfCategories()" method exacly inside the builder:
void _loadListOfCategories(AsyncSnapshot<QuerySnapshot> snapshot) {
var temporalList = new List<CategoryModel>();
for (DocumentSnapshot doc in snapshot.data.documents) {
temporalList.add(new CategoryModel(
id: doc.documentID,
count: doc['Count'],
nome: doc['Nome']));
}
setState(() {
_listOfCategories = temporalList;
});
}
But I couldn't call setState() here becouse I'm actually inside the building method.
use a map, your key will be 'document.documentID' and the value a boolean.
Map map = Map() // assuming this a string document.documentID
checked: map.get(document.documentID),
in your checkbox you call
setState()=>
map.put(document.documentID, !map.get(document.documentID));