initializing class as model inside BlocBuilder and getting error - dart

in my application i have model as this below structure and i want to initializing that inside BlocBuilder to update ui elements such as Text(), for example:
class _Login extends State<Login> {
UserInfo _userInfo = UserInfo();
LoginListingBloc loginListingBloc;
#override
void initState() {
loginListingBloc = BlocProvider.of<LoginListingBloc>(context);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder(
bloc: loginListingBloc,
builder: (BuildContext context, LoginListingStates state) {
if (state is LoginUninitializedState) {
} else if (state is LoginFetchedState) {
_userInfo = state.userInfo;
print(_userInfo.name);
}
return Text("My Username from server is: $_userInfo.name");
},
),
);
}
}
i don't want to make some variable such as name,email or avatart and fill them inside BlocBuilder to use them because i have this class model and i think i can initialize inside BlocBuilder to use that,
unfortunately i get this error when i run application:
The following ArgumentError was thrown building
BlocBuilder(dirty,
dependencies: [MediaQuery], state:
_BlocBuilderBaseState#2342d): Invalid argument(s)
LoginListingStates class content:
abstract class LoginListingStates {}
class LoginUninitializedState extends LoginListingStates {}
class LoginFetchingState extends LoginListingStates {}
class LoginFetchedState extends LoginListingStates {
final UserInfo userInfo;
LoginFetchedState({this.userInfo}) : assert(userInfo != null);
}
class LoginErrorState extends LoginListingStates {}
and my UserInfo model class:
class UserInfo {
String _name;
String _email;
String _avatar;
UserInfo();
UserInfo.fromJson(Map<String, dynamic> json)
: _name = json["name"],
_email = json["email"],
_avatar = json["avatar"],
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = _name;
data['email'] = _email;
data['avatar'] = _avatar;
return data;
}
String get email => _email;
String get name => _name;
int get avatar=> _avatar;
}

Related

How do I create an abstract factory?

Is it possible to somehow create an abstract factory method? Maybe what I'm trying to do is possible to implement differently?
abstract class ApiModel {
// Error: A function body must be provided.
factory ApiModel.fromJson(Map<String, dynamic> json);
}
class User extends ApiModel {
final int id;
final String name;
User({required this.id, required this.name});
#override
factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as int,
name: json['name'] as String,
);
}
}
class ApiResponse<Model extends ApiModel> {
final List<Model> results;
ApiResponse({required this.results});
factory ApiResponse.fromJson(Map<String, dynamic> json) {
return ApiResponse(results: (json['results'] as List).map((item) => Model.fromJson(item)).toList());
}
}
I solved it like this:
factory ApiResponse.fromJson(Map<String, dynamic> json, Model Function(dynamic) mapper) {
return ApiResponse(
info: Info.fromJson(json['info']),
results: (json['results'] as List).map(mapper).toList(),
);
}

The getter 'pokemon' was called on null. Receiver: null Tried calling: pokemon

I am trying to make a pokemon app. But I get this error:
"The getter 'pokemon' was called on null.
Receiver: null
Tried calling: pokemon"
I am putting error page to here.
I am using this API url for data "https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json"
And my Pokemon class is here:**
class Pokedex {
List<Pokemon> pokemon;
Pokedex({
this.pokemon});
Pokedex.fromJson(dynamic json) {
if (json["pokemon"] != null) {
pokemon = [];
json["pokemon"].forEach((v) {
pokemon.add(Pokemon.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
if (pokemon != null) {
map["pokemon"] = pokemon.map((v) => v.toJson()).toList();
}
return map;
}
}
class Pokemon {
int id;
String num;
String name;
String img;
List<String> type;
String height;
String weight;
String candy;
int candyCount;
String egg;
double spawnChance;
int avgSpawns;
String spawnTime;
List<double> multipliers;
List<String> weaknesses;
List<Next_evolution> nextEvolution;
Pokemon({
this.id,
this.num,
this.name,
this.img,
this.type,
this.height,
this.weight,
this.candy,
this.candyCount,
this.egg,
this.spawnChance,
this.avgSpawns,
this.spawnTime,
this.multipliers,
this.weaknesses,
this.nextEvolution});
Pokemon.fromJson(dynamic json) {
id = json["id"];
num = json["num"];
name = json["name"];
img = json["img"];
type = json["type"] != null ? json["type"].cast<String>() : [];
height = json["height"];
weight = json["weight"];
candy = json["candy"];
candyCount = json["candy_count"];
egg = json["egg"];
spawnChance = json["spawn_chance"];
avgSpawns = json["avg_spawns"];
spawnTime = json["spawn_time"];
multipliers = json["multipliers"] != null ? json["multipliers"].cast<double>() : [];
weaknesses = json["weaknesses"] != null ? json["weaknesses"].cast<String>() : [];
if (json["next_evolution"] != null) {
nextEvolution = [];
json["next_evolution"].forEach((v) {
nextEvolution.add(Next_evolution.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
map["id"] = id;
map["num"] = num;
map["name"] = name;
map["img"] = img;
map["type"] = type;
map["height"] = height;
map["weight"] = weight;
map["candy"] = candy;
map["candy_count"] = candyCount;
map["egg"] = egg;
map["spawn_chance"] = spawnChance;
map["avg_spawns"] = avgSpawns;
map["spawn_time"] = spawnTime;
map["multipliers"] = multipliers;
map["weaknesses"] = weaknesses;
if (nextEvolution != null) {
map["next_evolution"] = nextEvolution.map((v) => v.toJson()).toList();
}
return map;
}
}
class Next_evolution {
String num;
String name;
Next_evolution({
this.num,
this.name});
Next_evolution.fromJson(dynamic json) {
num = json["num"];
name = json["name"];
}
Map<String, dynamic> toJson() {
var map = <String, dynamic>{};
map["num"] = num;
map["name"] = name;
return map;
}
}
And this is my main.dart file:
import 'package:flutter/material.dart';
import 'package:pokemon_api/pokemon_list.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PokemonList(),
);
}
}
And this is my pokemon_list.dart file:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'model/pokedex.dart';
class PokemonList extends StatefulWidget {
const PokemonList({Key key}) : super(key: key);
#override
_PokemonListState createState() => _PokemonListState();
}
class _PokemonListState extends State<PokemonList> {
String url =
"https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json";
Pokedex pokedex;
var response;
Future<Pokedex> PokemonlariGetir() async {
response= await http.get(Uri.parse(url)).then((value){
var decodedJson = json.decode(response.body);
pokedex = Pokedex.fromJson(decodedJson);
});
debugPrint(pokedex.toString());
return pokedex;
}
#override
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Pokemon"),
),
body: FutureBuilder(
future: PokemonlariGetir(),
builder: (context, AsyncSnapshot<Pokedex> gelenPokedex) {
if (gelenPokedex.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else if (gelenPokedex.connectionState==ConnectionState.done) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2),
itemBuilder: (context,index){
return Text(gelenPokedex.data.pokemon[index].name);
});
// return GridView.count(crossAxisCount: 2,children:gelenPokedex.data.pokemon.map((poke){
// return Text(poke.name);
// }).toList(),);
}else{
return Center(child: CircularProgressIndicator(),);
}
}),
);
}
}

What is an equivalent for Dart 2 to `typeof` of TypeScript?

I'm new to Dart 2. I want a class to have a property. It's a reference of other class. it's not an instance but class itself. In TypeScript, it's possible to write as below. Is there a same way in Dart 2?
class Item { }
class ItemList {
itemClass: typeof Item;
}
const itemList = new ItemList();
itemList.itemClass = Item;
UPDATED:
I added some more context. The following is minimal sample code. I want to delegate a role of instantiation to super class.
class RecordBase {
id = Math.random();
toJson() {
return { "id": this.id };
};
}
class DbBase {
recordClass: typeof RecordBase;
create() {
const record = new this.recordClass();
const json = record.toJson();
console.log(json);
}
}
class CategoryRecord extends RecordBase {
toJson() {
return { "category": "xxxx", ...super.toJson() };
};
}
class TagRecord extends RecordBase {
toJson() {
return { "tag": "yyyy", ...super.toJson() };
};
}
class CategoryDb extends DbBase {
recordClass = CategoryRecord;
}
class TagDb extends DbBase {
recordClass = TagRecord;
}
const categoryDb = new CategoryDb();
categoryDb.create();
const tagDb = new TagDb();
tagDb.create();
I have tried to make you sample code into Dart. As I told before, you cannot get a reference to a class and call the constructor on runtime based on this reference.
But you can make a reference to a method which constructs the object of you class.
import 'dart:math';
class RecordBase {
static final Random _rnd = Random();
final int id = _rnd.nextInt(100000);
Map<String, dynamic> toJson() => <String, dynamic>{'id': id};
}
abstract class DbBase {
final RecordBase Function() getRecordClass;
RecordBase record;
Map<String, dynamic> json;
DbBase(this.getRecordClass);
void create() {
record = getRecordClass();
json = record.toJson();
print(json);
}
}
class CategoryRecord extends RecordBase {
#override
Map<String, dynamic> toJson() {
return <String, dynamic>{'category': 'xxxx', ...super.toJson()};
}
}
class TagRecord extends RecordBase {
#override
Map<String, dynamic> toJson() {
return <String, dynamic>{'tag': 'yyyy', ...super.toJson()};
}
}
class CategoryDb extends DbBase {
CategoryDb() : super(() => CategoryRecord());
}
class TagDb extends DbBase {
TagDb() : super(() => TagRecord());
}
void main() {
final categoryDb = CategoryDb();
categoryDb.create(); // {category: xxxx, id: 42369}
final tagDb = TagDb();
tagDb.create(); // {tag: yyyy, id: 97809}
}
I am not really sure if the create() method should be seen as a method or a constructor. So I choose to make it a method to be closer to your code.

Get data from JSON and add into the List on initState()

I have JSON file below.
Data.json
[{
"rownum": 1,
"total": 10.99793271,
"total2": 106.65666751,
}, {
"rownum": 2,
"total": 10.99793271,
"total2": 106.65666751,
}]
and the class Item and List
List <Item> item;
class Item {
String row;
String total;
String total2;
Student({this.row, this.total, this.total2});
}
How can I get data from data.json and add them into List <Item> item on the initState()?
Like this
class MyAppState extends State<MyApp> {
#override
void initState() {
Future<String> _loadAStudentAsset() async {
return await rootBundle.loadString('assets/data.json');
}
//....some code to add value into list
super.initState();
}
That solution is also valid for you:
Flutter: How to display a short text file from assets on screen of phone?
If we make another example with same template:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
void main() {
runApp(Test());
}
class Test extends StatefulWidget {
#override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
Future _future;
Future<String> loadString() async =>
await rootBundle.loadString('assets/data.json');
#override
void initState() {
_future = loadString();
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: FutureBuilder(
future: _future,
builder: (context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Text('Loading...');
}
List<dynamic> parsedJson = jsonDecode(snapshot.data);
items = parsedJson.map((element) {
return Item(
row: element['rownum'].toString(),
total: element['total'].toString(),
total2: element['total2'].toString(),
);
}).toList();
;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return Column(
children: <Widget>[
Text(item.row),
Text(item.total),
Text(item.total2),
],
);
},
);
},
),
),
);
}
}
List<Item> items;
class Item {
String row;
String total;
String total2;
Item({this.row, this.total, this.total2});
}
class Item {
static final String db_row = "rownum";
static final String db_total = "total";
static final String db_total2 = "total2";
int row;
double total;
double total2;
Item({this.row, this.total, this.total2});
Item.fromMap(Map map) {
this.row = map[Item.db_row];
this.total = map[Item.db_total];
this.total2 = map[Item.db_total2];
}
Map toMap() =>
{Item.db_row: row, Item.db_total: total, Item.db_total2: total2};
static List<Item> fromMapList(mapList) {
List<Item> items = new List();
new List.from(mapList).forEach((mapItem) {
items.add(Item.fromMap(mapItem));
});
return items;
}
}
And
List <Item> items = Item.fromMapList(await rootBundle.loadString('assets/data.json'));

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)),
)
],
);
}
}

Resources