Fetch Data from a Wix Database through http request - dart

I created a database with Wix that has several different types of content like Strings, images, addresses and more.I want to use the information from the WIX database for an app(made with Flutter and Dart); simply portraying the information in a ListView but it seems the data doesnt reach the app.
I created the necessary function on Wix to make the database accessible for third parties and tested it with Postman. When i make the request with this Url (https://daudadmin.editorx.io/acteeventpage/_functions/regions) it works fine and Postman returns the items with all the information as JSON.
Now when i use the Url in my app code; it just returns a blank page. That is the code i use currently:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final url = 'https://daudadmin.editorx.io/acteeventpage/_functions/regions';
var _postsJson = [];
void fetchData() async {
try {
final response = await get(Uri.parse(url));
final jsonData = jsonDecode(response.body) as List;
setState(() {
_postsJson = jsonData;
});
} catch (err) {
//handle error here with error message
}
}
#override
void initState() {
// TODO: implement initState
super.initState();
fetchData();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView.builder(
itemCount: _postsJson.length,
itemBuilder: (context, i) {
final post = _postsJson[i];
return Text("Title: ${post["title"]}");
}),
),
);
}
}

This is the line causing your error
final jsonData = jsonDecode(response.body) as List;
The data being returned from the endpoint is a Map and not a List.
{
"items": [
{},
...
],
}
To access the list, try accessing the items property of the map by changing the declaration to
final jsonData = jsonDecode(response.body)["items"] as List;

Related

Flutter integrating Hive database with Riverpod

There is very easy way to use Hive key-value database on StatefulWidgets, for example:
class HookDemo extends StatefulWidget {
#override
_HookDemoState createState() => _HookDemoState();
}
class _HookDemoState extends State<HookDemo> {
Box user;
#override
void initState() {
super.initState();
user = Hive.box<User>('user');
}
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
final _u = User()
..nameFamily = 'myname'
..mobileNumber = '123456789';
_user!.add(_u);
_u.save();
},
child: Icon(Icons.add),
),
...
);
}
}
here we defined Box user property and inside initState we implemented what's user such as user = Hive.box<User>('user');
after that we can use user without any problem and getting already opened error
now in this current application we used HookWidget and when we want to use Hive we get error as box already opened
main.dart:
Future<void> initHiveDriver() async {
final appDocumentDirectory = await path_provider.getApplicationDocumentsDirectory();
await Hive.initFlutter(appDocumentDirectory.path);
await Hive.openBox<UserAdapter>('user');
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
///...
initHiveDriver();
runApp(
ProviderScope(observers: [
Logger()
],
child: MyApp()),
);
}
how can i create a provider for Hive with Riverpod and use it into HookWidget?
I am using Hive with Riverpod like this.
I am using a named constructor so I can await the openBox call.
final hiveProvider = FutureProvider<HiveDB>((_) => HiveDB.create());
class HiveDB {
var _userBox;
HiveDB._create() {}
static Future<HiveDB> create() async {
final component = HiveDB._create();
await component._init();
return component;
}
_init() async {
Hive.registerAdapter(UserAdapter());
this._userBox = await Hive.openBox<User>('user');
}
storeUser(User user) {
this._userBox.put('user', user);
}
User getUser() {
return this._userBox.get('user');
}
}
Use in a ConsumerWidget:
class SomeWidget extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final provider = ref.watch(hiveProvider).data?.value;
...
}
}

Shared Preferences on iOS

I'm trying to use Shared Preferences but I have this strange behavior.
Im using:
shared_preferences: ^0.5.6+2
On a StateFull Widget on the initState I have this:
#override
void initState() {
super.initState();
_testing()
}
Future<void> _testing() async{
print("starting")
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setInt('value', 1);
print("saved?")
}
When I run the code with flutter run, I only get the print for "starting", not for "saved?".
The same exact code Im using for this project I've used to build an Android App and it worked just fine. But for iOS is not working.
What I am missing?
It works, there are no errors. Copy & paste and try it again:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
saveValue() async{
print("Starting...");
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setInt('value', 1);
print("Saved?");
}
#override
void initState() {
super.initState();
saveValue();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text("Shared Preferences")),
),
);
}
}
It won't print because you are using the await before setting the int value.
await prefs.setInt('value', 1);
If you remove the await , then try checking the print value it will work fine.
Let me know if it works.

How to make a Sink<Locale> to format the result of a Stream<String>?

In google IO 18, the Flutter presenters have showed a feature but have not showed how to implement this.
The video (at exact time) is: https://youtu.be/RS36gBEp8OI?t=1776
How to implement such thing? How can I properly make the Stream to be correctly formatted based on a Sink?
(sorry but I am not too familiar with Rx)
Use the combineLatest function from the rxdart package. It takes the latest values of input streams, so any time either the locale or cart items change it will calculate and format the total cost.
import 'dart:async'; // Sink, Stream
import 'dart:ui'; // Locale
import 'package:rxdart/rxdart.dart'; // Observable, *Subject
class Bloc {
var _locale = BehaviorSubject<Locale>(seedValue: Locale('en', 'US'));
var _items = BehaviorSubject<List<CartItem>>(seedValue: []);
Stream<String> _totalCost;
Sink<Locale> get locale => _locale.sink;
Stream<List<CartItem>> get items => _items.stream;
Stream<String> get totalCost => _totalCost;
Bloc() {
_totalCost = Observable.combineLatest2<Locale, List<CartItem>, String>(
_locale, _items, (locale, items) {
// TODO calculate total price of items and format based on locale
return 'USD 10.00';
}).asBroadcastStream();
}
void dispose() {
_locale.close();
_items.close();
}
}
Disclaimer: I didn't try to run this code so there might be errors but the basic idea should be solid.
The best candidate for doing this cross-platform is NumberFormat from the intl package. However you still have to pass it a locale string ("en_US") and ISO 4217 currency code ("USD").
After a little digging I couldn't find this information in any Dart package. The NumberFormat class has a private map for looking up a currency symbol ("$") from a currency code, but keys of the map, the currency codes, are inaccessible. So I decided to make a package that makes locale strings and currency codes available.
currency_bloc.dart
import 'dart:async';
import 'package:rxdart/rxdart.dart';
import 'package:intl/intl.dart';
import 'package:locales/locales.dart';
import 'package:locales/currency_codes.dart';
class LocalCurrency {
const LocalCurrency(this.locale, this.code);
final Locale locale;
final CurrencyCode code;
#override toString() => '$code ($locale)';
#override operator==(o) => o is LocalCurrency && o.locale == locale && o.code == code;
#override hashCode => toString().hashCode;
}
/// Emits currency strings according to a locale.
class CurrencyBloc {
// Inputs.
final _valueController = StreamController<double>();
final _currencyController = StreamController<LocalCurrency>();
// Outputs.
final _currency = BehaviorSubject<String>();
/// The last formatted currency value emitted from the output stream.
String lastCurrency;
// For synchronously receiving the latest inputs.
double _value;
NumberFormat _formatter;
CurrencyBloc({LocalCurrency initialCurrency, double initialValue}) {
_valueController.stream
.distinct()
.listen((value) => _updateCurrency(value: value));
_currencyController.stream
.distinct()
.listen((currency) => _updateCurrency(currency: currency));
// Initialize inputs.
locale.add(initialCurrency ??
LocalCurrency(Locale.en_US, CurrencyCode.usd));
value.add(initialValue ?? 0.0);
}
void dispose() {
_valueController.close();
_currencyController.close();
_currency.close();
}
_updateCurrency({double value, LocalCurrency currency}) {
if (currency != null) {
_formatter = NumberFormat.simpleCurrency(
locale: '${currency.locale}',
name: '${currency.code}',
decimalDigits: 2);
}
if (value != null) {
_value = value;
}
if (_value != null && _formatter != null) {
lastCurrency = _formatter.format(_value);
_currency.add(lastCurrency);
}
}
/// Change the current [Locale] and/or [CurrencyCode].
Sink<LocalCurrency> get locale => _currencyController.sink;
/// Change the the value to be formatted.
Sink<double> get value => _valueController.sink;
/// Formatted currency.
Stream<String> get currency => _currency.stream;
}
currency_provider.dart (conventional)
class CurrencyProvider extends InheritedWidget {
CurrencyProvider({Key key, #required this.bloc, #required Widget child})
: super(key: key, child: child);
final CurrencyBloc bloc;
#override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static CurrencyBloc of(BuildContext context) =>
(context.inheritFromWidgetOfExactType(CurrencyProvider) as CurrencyProvider)
.bloc;
}
Example usage
...
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CurrencyBloc bloc;
#override
Widget build(BuildContext context) =>
CurrencyProvider(bloc: bloc, child: CurrencyExample());
#override
void initState() {
super.initState();
bloc = CurrencyBloc();
}
#override
void dispose() {
bloc.dispose();
super.dispose();
}
#override
void didUpdateWidget(StatefulWidget oldWidget) {
super.didUpdateWidget(oldWidget);
bloc.dispose();
bloc = CurrencyBloc();
}
}
class CurrencyExample extends StatelessWidget {
final controller = TextEditingController();
#override
Widget build(BuildContext context) {
final bloc = CurrencyProvider.of(context);
return ListView(
children: <Widget>[
TextField(controller: controller),
StreamBuilder(
stream: bloc.currency,
initialData: bloc.lastCurrency,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data);
} else if (snapshot.hasError) {
return new Text('${snapshot.error}');
}
return Center(child: CircularProgressIndicator());
}),
FlatButton(
child: Text('Format Currency'),
onPressed: () => bloc.value.add(double.tryParse(controller.text)),
)
],
);
}
}

Remote Config Device Language Changes in Flutter

I am encountering a problem, where localization works fine, but the applications needs to be restarted in order for the changes to propagate.
Orientation changes
I know about OrientationBuilder, which will call its builder whenever it detects a change in the device's orientation, which in e.g. Android would be considered as a configuration change, just like device language changes.
Language changes
Is there something like LanguageBuilder? I could not find anything on my own and not on flutter.io nor on pub. I have read this tutorial and know about Locale, but I do not see a Stream for Locale.
My problem is that changing the language in iOS and Android native is really smooth. It gets handled automatically and perfectly integrates with services like Firebase Remote Config.
I really wonder if there is some method that will allow me to refresh my localization.
Question
So I am asking how I can refresh my Remote Config when the device language changes.
No there's no Builder for Locale.
Instead, there's an InheritedWidget which you can subscribe to using Localizations.of.
Since it is an InheritedWidget, all widgets that call Localizations.of will automatically refresh on locale change.
EDIT :
A example on how to live reload text using Flutter Locale system :
Let's assume you have the following class that holds translations :
class MyData {
String title;
MyData({this.title});
}
You'd then have a LocalizationsDelegate that contains such data. A dumb implementation would be the following :
class MyLocale extends LocalizationsDelegate<MyData> {
MyData data;
MyLocale(this.data);
#override
bool isSupported(Locale locale) {
return true;
}
#override
Future<MyData> load(Locale locale) async {
return data;
}
#override
bool shouldReload(MyLocale old) {
return old.data != data;
}
}
To use it simply pass it to MaterialApp.localizationsDelegates (be sure to add flutter_localizations to your pubspec.yaml) :
LocalizationsDelegate myLocale = MyLocale(MyData(title: "Foo"));
...
MaterialApp(
localizationsDelegates: [
myLocale,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
);
You can then freely live reload your translations by replacing myLocale with a new MyLocale instance.
Here's a full example of a click counter app. But where the current count is instead stored inside Locale (because why not ?)
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
class MyCount {
String count;
MyCount({this.count});
}
class MyCountLocale extends LocalizationsDelegate<MyCount> {
MyCount data;
MyCountLocale(this.data);
#override
bool isSupported(Locale locale) {
return true;
}
#override
Future<MyCount> load(Locale locale) async {
return data;
}
#override
bool shouldReload(MyCountLocale old) {
return old.data != data;
}
}
Future<void> main() async {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ValueNotifier<int> count = ValueNotifier<int>(0);
LocalizationsDelegate myLocale;
#override
void initState() {
count.addListener(() {
setState(() {
myLocale = MyCountLocale(MyCount(count: count.value.toString()));
});
});
myLocale = MyCountLocale(MyCount(count: count.value.toString()));
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
myLocale,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
home: MyHomePage(count: count),
);
}
}
class MyHomePage extends StatefulWidget {
final ValueNotifier<int> count;
MyHomePage({this.count});
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
primary: true,
appBar: AppBar(),
body: Column(
children: <Widget>[
FloatingActionButton(
onPressed: () => widget.count.value++,
child: Icon(Icons.plus_one),
),
ListTile(
title: Text(Localizations.of<MyCount>(context, MyCount).count),
),
],
),
);
}
}
Device language changes can be detected using a WidgetsBindingObserver.
It is the simplest to use it with a StatefulWidget in your State (with WidgetsBindingObserver):
class _MyWidgetState extends State<MyWidget> with WidgetsBindingObserver {
#override
void didChangeLocales(List<Locale> locale) {
// The device language was changed when this is called.
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
...
}
This means that you can now reload your RemoteConfig in didChangeLocales:
#override
void didChangeLocales(List<Locale> locale) {
_updateRemoteConfig();
}
Future<void> _updateRemoteConfig() async {
final remoteConfig = await RemoteConfig.instance;
await remoteConfig.activateFetched(); // This will apply the new locale.
}

How to get Flutter Firebase Storage in a separate method?

I've successfully saved an image to my Firebase Storage reference. Now I need to download it. The examples I've seen are uploading and downloading in the same method, using the same StorageUploadTask with this line of code...
final Uri downloadUrl = (await uploadTask.future).downloadUrl;
My question is how can I get the downloadUrl from a separate method that doesn't require an uploadTask.future since I'm only uploading an image when a FirebaseUser updates their profile image?
StorageReference now has Future<dynamic> getDownloadURL() method. Retype result to String and use it with your NetworkImage widget:
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
class FirestoreImage extends StatefulWidget {
final StorageReference reference;
final Widget fallback;
final ImageProvider placeholder;
FirestoreImage(
{Key key,
#required this.reference,
#required this.fallback,
#required this.placeholder});
#override
FirestoreImageState createState() =>
FirestoreImageState(reference, fallback, placeholder);
}
class FirestoreImageState extends State<FirestoreImage> {
final Widget fallback;
final ImageProvider placeholder;
String _imageUrl;
bool _loaded = false;
_setImageData(dynamic url) {
setState(() {
_loaded = true;
_imageUrl = url;
});
}
_setError() {
setState(() {
_loaded = false;
});
}
FirestoreImageState(
StorageReference reference, this.fallback, this.placeholder) {
reference.getDownloadURL().then(_setImageData).catchError((err) {
_setError();
});
}
#override
Widget build(BuildContext context) => _loaded
? FadeInImage(
image: NetworkImage(_imageUrl),
placeholder: placeholder,
)
: fallback;
}
Old Answer:
I've just started developing in Flutter (Dart) so my answer will definitely not be perfect (maybe even bad) but here is how I did it:
import 'dart:typed_data';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
class FirestoreImage extends StatefulWidget {
final StorageReference _reference;
FirestoreImage(this._reference);
#override
FirestoreImageState createState() => FirestoreImageState(_reference);
}
class FirestoreImageState extends State<FirestoreImage> {
Uint8List _imageData;
_setImageData(Uint8List data) {
setState(() {
_imageData = data;
});
}
FirestoreImageState(StorageReference reference) {
reference
.getData(0x3FFFFFFF)
.then(_setImageData)
.catchError((err) {});
}
#override
Widget build(BuildContext context) =>
_imageData == null ? Container() : Image.memory(_imageData);
}
Now you can display FirestoreImage by calling new FirestoreImage(imageStorageReference). Maybe there is better way by extending Image
VizGhar provided a nice solution.
I've cleaned up the class, added some features and documentation.
It's available on this gist as well.
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
enum ImageDownloadState { Idle, GettingURL, Downloading, Done, Error }
class FirebaseStorageImage extends StatefulWidget {
/// The reference of the image that has to be loaded.
final StorageReference reference;
/// The widget that will be displayed when loading if no [placeholderImage] is set.
final Widget fallbackWidget;
/// The widget that will be displayed if an error occurs.
final Widget errorWidget;
/// The image that will be displayed when loading if no [fallbackWidget] is set.
final ImageProvider placeholderImage;
FirebaseStorageImage(
{Key key,
#required this.reference,
#required this.errorWidget,
this.fallbackWidget,
this.placeholderImage}) {
assert(
(this.fallbackWidget == null && this.placeholderImage != null) ||
(this.fallbackWidget != null && this.placeholderImage == null),
"Either [fallbackWidget] or [placeholderImage] must not be null.");
}
#override
_FirebaseStorageImageState createState() => _FirebaseStorageImageState(
reference, fallbackWidget, errorWidget, placeholderImage);
}
class _FirebaseStorageImageState extends State<FirebaseStorageImage>
with SingleTickerProviderStateMixin {
_FirebaseStorageImageState(StorageReference reference, this.fallbackWidget,
this.errorWidget, this.placeholderImage) {
var url = reference.getDownloadURL();
this._imageDownloadState = ImageDownloadState.GettingURL;
url.then(this._setImageData).catchError((err) {
this._setError();
});
}
/// The widget that will be displayed when loading if no [placeholderImage] is set.
final Widget fallbackWidget;
/// The widget that will be displayed if an error occurs.
final Widget errorWidget;
/// The image that will be displayed when loading if no [fallbackWidget] is set.
final ImageProvider placeholderImage;
/// The image that will be/has been downloaded from the [reference].
Image _networkImage;
/// The state of the [_networkImage].
ImageDownloadState _imageDownloadState = ImageDownloadState.Idle;
/// Sets the [_networkImage] to the image downloaded from [url].
void _setImageData(dynamic url) {
this._networkImage = Image.network(url);
this
._networkImage
.image
.resolve(ImageConfiguration())
.addListener((_, __) {
if (mounted)
setState(() => this._imageDownloadState = ImageDownloadState.Done);
});
if (this._imageDownloadState != ImageDownloadState.Done)
this._imageDownloadState = ImageDownloadState.Downloading;
}
/// Sets the [_imageDownloadState] to [ImageDownloadState.Error] and redraws the UI.
void _setError() {
if (mounted)
setState(() => this._imageDownloadState = ImageDownloadState.Error);
}
#override
Widget build(BuildContext context) {
switch (this._imageDownloadState) {
case ImageDownloadState.Idle:
case ImageDownloadState.GettingURL:
case ImageDownloadState.Downloading:
return Image(image: this.placeholderImage) ?? this.fallbackWidget;
case ImageDownloadState.Error:
return this.errorWidget;
case ImageDownloadState.Done:
return this._networkImage;
break;
default:
return this.errorWidget;
}
}
}
Not possible (yet). You need to store that uri yourself inside a database.
But you may and should use getData instead of using a download url within a firebase app.

Resources