How to get the specific data through api by using flutter_bloc - flutter

I am beginner in flutter and working on fetching the specific data by using flutter_bloc package. I have successfully fetch the api data by using flutter_bloc in HomePage but how do i fetch the more specific data.For example in Home Page it fetch the data when i open the app and there is a button at the bottom which moves to new screen that is a Settings Screen which has Two radio buttons and one Raised Button named as Save.When i select any of the radiobutton and click on save button it should moves back to the homepage and calls the api and update the data which was already fetched in homepage. Below is the dart code and bloc code, it will be lengthy but hope you understand my code
Main.dart
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<PrayerBloc>(
create: (BuildContext context) => PrayerBloc(repository: PrayerRepositoryImpl()),
),
BlocProvider<MethodBloc>(
create: (BuildContext context) => MethodBloc(methodRepository: MethodRepositoryImpl()),
),
],
child: HomePage(),
);
HomePage.dart
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
PrayerBloc prayerBloc;
#override
void initState() {
super.initState();
prayerBloc = BlocProvider.of<PrayerBloc>(context);
prayerBloc.add(FetchPrayerEvent());
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) {
return Material(
child: Scaffold(
appBar: AppBar(
title: Text("Prayer API"),
),
body: Container(
child: BlocListener<PrayerBloc, PrayerState>(
listener: (context, state) {
if (state is PrayerErrorState) {
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
),
);
}
},
child: BlocBuilder<PrayerBloc, PrayerState>(
builder: (context, state) {
if (state is InitialPrayerState) {
return buildLoading();
} else if (state is PrayerLoadingState) {
return buildLoading();
} else if (state is PrayerLoadedState) {
return buildArticleList(state.item);
} else if (state is PrayerErrorState) {
return buildErrorUi(state.message);
}
},
),
),
),
),
);
},
),
);
}
Widget buildLoading() {
return Center(
child: CircularProgressIndicator(),
);
}
Widget buildErrorUi(String message) {
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
message,
style: TextStyle(color: Colors.red),
),
),
);
}
Widget buildArticleList(List<Item> item) {
return ListView.builder(
itemCount: item == null ? 0 : item.length,
itemBuilder: (BuildContext ctx, int pos) {
return new Container(
child: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Card(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: new Container(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10.0),
),
Row(
children: <Widget>[
Text("Fajr"),
Padding(
padding: EdgeInsets.only(left: 50.0),
),
Text(item[pos].fajr),
],
),
Row(
children: <Widget>[
Text("Dhuhr"),
Padding(
padding: EdgeInsets.only(left: 30.0),
),
Text(item[pos].dhuhr),
],
),
Builder(
builder: (context)=>
RaisedButton(
onPressed: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SettingsPage()),
);
},
),
)
],
),
),
),
)
],
),
),
);
},
);
}
Prayer_bloc.dart
class PrayerBloc extends Bloc<PrayerEvent, PrayerState> {
PrayerRepository repository;
PrayerBloc({#required this.repository});
#override
PrayerState get initialState => InitialPrayerState();
#override
Stream<PrayerState> mapEventToState(
PrayerEvent event,
) async* {
if (event is FetchPrayerEvent) {
yield PrayerLoadingState();
try {
List<Item> item = await repository.getItem();
yield PrayerLoadedState(item: item);
} catch (e) {
yield PrayerErrorState(message: e.toString());
}
}
}
}
PrayerEvent.dart
abstract class PrayerEvent extends Equatable {}
class FetchPrayerEvent extends PrayerEvent {
#override
// TODO: implement props
List<Object> get props => null;
}
PrayerState.dart
abstract class PrayerState extends Equatable {
const PrayerState();
}
class InitialPrayerState extends PrayerState {
#override
List<Object> get props => [];
}
class PrayerLoadingState extends PrayerState {
#override
List<Object> get props => [];
}
class PrayerLoadedState extends PrayerState {
List<Item> item;
PrayerLoadedState({#required this.item});
#override
List<Object> get props => null;
}
class PrayerErrorState extends PrayerState {
String message;
PrayerErrorState({#required this.message});
#override
List<Object> get props => [message];
}
PrayerRepository.dart
abstract class PrayerRepository {
Future<List<Item>> getItem();
}
class PrayerRepositoryImpl implements PrayerRepository {
#override
Future<List<Item>> getItem() async {
var response = await http.get("https://muslimsalat.com/riyadh.json?key=");
if (response.statusCode == 200) {
var data = json.decode(response.body);
List<Item> item = Welcome.fromJson(data).items;
return item;
} else {
throw Exception();
}
}
}
So these dart code fetch the data from api and load in HomePage when i open the application.Now the second page which is settings page, below is the code
SettingsPage.dart
class SettingsPage extends StatefulWidget {
#override
_SettingsPageState createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
int selectedRadio;
#override
void initState() {
super.initState();
selectedRadio=0;
}
setSelectedRadio(int val){
setState(() {
selectedRadio=val;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: ListView(
children: <Widget>[
BlocBuilder<MethodBloc,MethodState>(
builder: (context,state){
return Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
padding: EdgeInsets.all(15.0),
child: Text(
"Prayer Methods",
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
Column(
children: <Widget>[
RadioListTile(
value: 1,
groupValue: selectedRadio,
activeColor: Colors.black,
title: Text(
"Egyptian General Authority of Survey",
),
onChanged: (val) {
print(val);
setSelectedRadio(val);
}),
RadioListTile(
value: 2,
groupValue: selectedRadio,
activeColor: Colors.black,
title: Text(
"University Of Islamic Sciences, Karachi (Shafi)",
),
onChanged: (val) {
print(val);
setSelectedRadio(val);
}),
FloatingActionButton(
onPressed: (){
Navigator.pop(context);
BlocProvider.of<MethodBloc>(context).add(MethodChangedEvent(method: selectedRadio)); //I have try this code in onpressed but unfortunately not succeed
print(selectedRadio);
},
child: Text('Save')
)
],
),
],
);
},
)
],
),
),
);
}
}
MethodBloc.dart
class MethodBloc extends Bloc<MethodEvent, MethodState> {
MethodRepository methodRepository;
MethodBloc({#required this.methodRepository});
#override
MethodState get initialState => InitialMethodState();
#override
Stream<MethodState> mapEventToState(
MethodEvent event,
) async* {
if(event is MethodChangedEvent){
yield MethodLoadingState();
try {
List<Item> item = await methodRepository.getMethod(event.method);
yield MethodLoadedState(item: item);
} catch (e) {
yield MethodErrorState(message: e.toString());
}
}
}
}
MethodEvent.dart
abstract class MethodEvent extends Equatable {
const MethodEvent();
}
class MethodChangedEvent extends MethodEvent {
final int method;
MethodChangedEvent({this.method}) : assert(method != null);
#override
List<Object> get props => null;
}
MethodState.dart
abstract class MethodState extends Equatable {
const MethodState();
}
class InitialMethodState extends MethodState {
#override
List<Object> get props => [];
}
class MethodLoadingState extends MethodState {
#override
List<Object> get props => [];
}
class MethodLoadedState extends MethodState {
List<Item> item;
MethodLoadedState({#required this.item});
#override
List<Object> get props => null;
}
class MethodErrorState extends MethodState {
String message;
MethodErrorState({#required this.message});
#override
List<Object> get props => [message];
}
MethodRepository.dart
abstract class MethodRepository{
Future<List<Item>> getMethod(int method);
}
class MethodRepositoryImpl implements MethodRepository {
#override
Future<List<Item>> getMethod(int method) async {
var response = await http.get("https://muslimsalat.com/riyadh/$method.json?key=");
if (response.statusCode == 200) {
var data = json.decode(response.body);
List<Item> item = Welcome.fromJson(data).items;
return item;
} else {
throw Exception();
}
}
}
PrayerModel.dart
class Welcome {
String title;
String query;
String welcomeFor;
int method;
String prayerMethodName;
String daylight;
String timezone;
String mapImage;
String sealevel;
TodayWeather todayWeather;
String link;
String qiblaDirection;
String latitude;
String longitude;
String address;
String city;
String state;
String postalCode;
String country;
String countryCode;
List<Item> items;
int statusValid;
int statusCode;
String statusDescription;
Welcome({
this.title,
this.query,
this.welcomeFor,
this.method,
this.prayerMethodName,
this.daylight,
this.timezone,
this.mapImage,
this.sealevel,
this.todayWeather,
this.link,
this.qiblaDirection,
this.latitude,
this.longitude,
this.address,
this.city,
this.state,
this.postalCode,
this.country,
this.countryCode,
this.items,
this.statusValid,
this.statusCode,
this.statusDescription,
});
factory Welcome.fromJson(Map<String, dynamic> json) => Welcome(
title: json["title"],
query: json["query"],
welcomeFor: json["for"],
method: json["method"],
prayerMethodName: json["prayer_method_name"],
daylight: json["daylight"],
timezone: json["timezone"],
mapImage: json["map_image"],
sealevel: json["sealevel"],
todayWeather: TodayWeather.fromJson(json["today_weather"]),
link: json["link"],
qiblaDirection: json["qibla_direction"],
latitude: json["latitude"],
longitude: json["longitude"],
address: json["address"],
city: json["city"],
state: json["state"],
postalCode: json["postal_code"],
country: json["country"],
countryCode: json["country_code"],
items: List<Item>.from(json["items"].map((x) => Item.fromJson(x))),
statusValid: json["status_valid"],
statusCode: json["status_code"],
statusDescription: json["status_description"],
);
Map<String, dynamic> toJson() => {
"title": title,
"query": query,
"for": welcomeFor,
"method": method,
"prayer_method_name": prayerMethodName,
"daylight": daylight,
"timezone": timezone,
"map_image": mapImage,
"sealevel": sealevel,
"today_weather": todayWeather.toJson(),
"link": link,
"qibla_direction": qiblaDirection,
"latitude": latitude,
"longitude": longitude,
"address": address,
"city": city,
"state": state,
"postal_code": postalCode,
"country": country,
"country_code": countryCode,
"items": List<dynamic>.from(items.map((x) => x.toJson())),
"status_valid": statusValid,
"status_code": statusCode,
"status_description": statusDescription,
};
}
class Item {
String dateFor;
String fajr;
String shurooq;
String dhuhr;
String asr;
String maghrib;
String isha;
Item({
this.dateFor,
this.fajr,
this.shurooq,
this.dhuhr,
this.asr,
this.maghrib,
this.isha,
});
factory Item.fromJson(Map<String, dynamic> json) => Item(
dateFor: json["date_for"],
fajr: json["fajr"],
shurooq: json["shurooq"],
dhuhr: json["dhuhr"],
asr: json["asr"],
maghrib: json["maghrib"],
isha: json["isha"],
);
Map<String, dynamic> toJson() => {
"date_for": dateFor,
"fajr": fajr,
"shurooq": shurooq,
"dhuhr": dhuhr,
"asr": asr,
"maghrib": maghrib,
"isha": isha,
};
}
class TodayWeather {
int pressure;
String temperature;
TodayWeather({
this.pressure,
this.temperature,
});
factory TodayWeather.fromJson(Map<String, dynamic> json) => TodayWeather(
pressure: json["pressure"],
temperature: json["temperature"],
);
Map<String, dynamic> toJson() => {
"pressure": pressure,
"temperature": temperature,
};
}

Once you've defined the Model for the Stream in bloc, you can easily access the object using BlocBuilder. As demonstrated on the code you've shared, you're able to build a List with buildArticleList() from the Stream. To access a specific data, you can follow a similar approach for that object.

Related

Flutter BlocBuilder doesn't update my List of Inputs

I'm using Flutter Bloc to keep track of state changes in my PIN and Fingerprint Authentication.
Currently i have the problem that my List of current Pin Inputs just get updated for the first item in the List. All the following inputs don't update the UI, but the List keeps on growing.
My Authentication_Bloc:
abstract class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationPinState> {
AuthenticationBloc()
: super(AuthenticationPinState(
pinNumbers: const [], pinStatus: AuthenticationPINStatus.enter));
}
class AuthenticationPinBloc extends AuthenticationBloc {
List<int> pinNumbers = [];
AuthenticationPinBloc() : super() {
on<AuthenticationPinAddEvent>((event, emit) async {
try {
pinNumbers.add(event.pinNumber);
if (pinNumbers.length < 6) {
emit(state.copyWith(
pinNumbers: pinNumbers,
pinStatus: AuthenticationPINStatus.enter));
} else if (pinNumbers.length == 6) {
emit(state.copyWith(
pinNumbers: pinNumbers,
pinStatus: AuthenticationPINStatus.equals));
} else {
emit(state.copyWith(
pinNumbers: pinNumbers,
pinStatus: AuthenticationPINStatus.unequals));
await Future.delayed(
const Duration(seconds: 2),
() => emit(AuthenticationPinState(
pinNumbers: pinNumbers,
pinStatus: AuthenticationPINStatus.enter)));
}
} catch (e) {}
});
on<AuthenticationPinEraseEvent>((event, emit) async {});
on<AuthenticationFingerprintEvent>((event, emit) {});
}
}
My Authentication_State:
enum AuthenticationPINStatus { enter, equals, unequals }
abstract class AuthenticationState extends Equatable {
#override
List<Object?> get props => [];
}
// Waiting to see if the user is authenticated
class AuthenticationUninitialized extends AuthenticationState {}
// Sucessfully authenticated
class AuthenticationAuthenticated extends AuthenticationState {}
// Not authenticated
class AuthenticationUnauthenticated extends AuthenticationState {}
class AuthenticationLoading extends AuthenticationState {}
class AuthenticationPinState extends AuthenticationState {
final List<int> pinNumbers;
final AuthenticationPINStatus pinStatus;
AuthenticationPinState({required this.pinNumbers, required this.pinStatus});
AuthenticationPinState copyWith(
{required AuthenticationPINStatus pinStatus,
required List<int> pinNumbers}) {
return AuthenticationPinState(pinNumbers: pinNumbers, pinStatus: pinStatus);
}
int getPINCount() {
return pinNumbers.length;
}
#override
List<Object?> get props => [List.from(pinNumbers), pinStatus];
}
My Authentication_Event:
abstract class AuthenticationEvent extends Equatable {
const AuthenticationEvent([List props = const []]) : super();
}
abstract class AuthenticationPinEvent extends AuthenticationEvent {
final int pinNumber;
const AuthenticationPinEvent({required this.pinNumber});
#override
List<Object?> get props => [pinNumber];
}
class AuthenticationPinAddEvent extends AuthenticationPinEvent {
const AuthenticationPinAddEvent({required int pinNumber})
: super(pinNumber: pinNumber);
#override
List<Object?> get props => [pinNumber];
}
class AuthenticationPinEraseEvent extends AuthenticationPinEvent {
const AuthenticationPinEraseEvent() : super(pinNumber: -1);
#override
List<Object?> get props => [];
}
class AuthenticationFingerprintEvent extends AuthenticationEvent {
const AuthenticationFingerprintEvent();
#override
List<Object?> get props => [];
}
My Code calling the Bloc:
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: BlocProvider(
create: (context) => AuthenticationPinBloc(),
child: BlocListener<AuthenticationPinBloc, AuthenticationPinState>(
listener: (context, state) {
if (state.pinStatus == AuthenticationPINStatus.equals) {
// Validate PIN
} else if (state.pinStatus == AuthenticationPINStatus.unequals) {
// Retry Logic
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
height: 120,
),
Flexible(
flex: 2,
child: Text(authenticationByPINCode,
style: Theme.of(context)
.textTheme
.titleLarge
?.merge(const TextStyle(color: Colors.blueGrey))),
),
BlocBuilder<AuthenticationPinBloc, AuthenticationPinState>(
buildWhen: (previous, current) {
return previous.pinNumbers != current.pinNumbers;
},
builder: (context, state) {
return BlocProvider<AuthenticationPinBloc>.value(
value: context.read<AuthenticationPinBloc>(),
child: Flexible(
flex: 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(6, (index) {
return PinSphere(
input: index < state.getPINCount());
}),
)),
);
},
),
const SizedBox(
height: 80,
),
const Flexible(flex: 3, child: Numpad())
],
),
),
)),
);
}

Flutter how to access data in the instance of an object gotten from a bloc

I am trying to get the productID from the list of products in my cart bloc and then merge the list with the cart quantity so that the end result is this:
"aos": [
{
"product_id": 10,
"quantity": 1
},
{
"product_id": 11,
"quantity": 2
}
],
I have tried to encode the map as a json, but there is still no option for me to access the id of each product. Please can someone point me in the right direction?
My Checkout Screen:
class CheckoutScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
// final List<dynamic> aos;
return Scaffold(
appBar: AppBar(
backgroundColor: buttonBG,
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const ShippingAddress(),
const SizedBox(height: 20),
BlocBuilder<CartBloc, CartState>(
builder: (context, state) {
if (state is CartLoaded) {
Map aos = state.cart.productQuantity(state.cart.products);
var pio = aos.keys.toList();
String productInstance = json.encode(pio);
debugPrint(productInstance);
return Expanded(
child: SizedBox(
height: 400,
child: ListView.builder(
itemCount: aos.keys.length,
itemBuilder: (BuildContext context, int index) {
final pIndex = aos.keys.elementAt(index);
final qIndex = aos.values.elementAt(index);
return ProductCard.summary(
product: pIndex,
quantity: qIndex,
);
},
),
),
);
}
return const Text('Something went wrong');
},
),
],
));
}
}
Is there a way to achieve that because i am using bloc?
Below is my CartModel:
import 'package:afia4_shopping_app/logic/models/product_models.dart';
import 'package:equatable/equatable.dart';
class Cart extends Equatable {
final List<ProductModel> products;
const Cart({this.products = const <ProductModel>[]});
#override
List<Object?> get props => [products];
Map productQuantity(products) {
var quantity = {};
products.forEach((product) {
if (!quantity.containsKey(product)) {
quantity[product] = 1;
} else {
quantity[product] += 1;
}
});
return quantity;
}
}
For the cartBloc:
class CartBloc extends Bloc<CartEvent, CartState> {
CartBloc() : super(CartLoading()) {
on<LoadCart>(_onLoadCart);
on<AddProduct>(_onAddProduct);
on<RemoveProduct>(_onRemoveProduct);
}
void _onLoadCart(
LoadCart event,
Emitter<CartState> emit,
) async {
emit(CartLoading());
try {
await Future<void>.delayed(const Duration(seconds: 1));
emit(CartLoaded());
} catch (_) {
emit(CartError());
}
}
void _onAddProduct(
AddProduct event,
Emitter<CartState> emit,
) {
if (state is CartLoaded) {
try {
emit(
CartLoaded(
cart: Cart(
products: List.from((state as CartLoaded).cart.products)
..add(event.product),
),
),
);
} on Exception {
emit(CartError());
}
}
}
void _onRemoveProduct(
RemoveProduct event,
Emitter<CartState> emit,
) {
if (state is CartLoaded) {
try {
emit(
CartLoaded(
cart: Cart(
products: List.from((state as CartLoaded).cart.products)
..remove(event.product),
),
),
);
} on Exception {
emit(CartError());
}
}
}
}
For the productModel:
class ProductModel {
ProductModel(
{this.name,
this.id,
});
String? name;
int? id;
factory ProductModel.fromJson(Map<String, dynamic> json) => ProductModel(
name: json["name"],
id: json["id"],
Map<String, dynamic> toJson() => {
"name": name,
"id": id
};
List<ProductModel> productsFromJson(String str) => List<ProductModel>.from(
json.decode(str).map((x) => ProductModel.fromJson(x)));
String productsToJson(List<ProductModel> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
}
If you add this dependency: collection: ^1.16.0, and include that:
import "package:collection/collection.dart";
You can use the groupListsBy() method and run:
var group = cart.products.groupListsBy((element) => element.id).map((key, value) => MapEntry(key, value.length));
Which will give you a Map<int?, int> grouping product id to the quantity.
You can then present it as you wish in your UI.

Although there is no error in the application that I want to show the weather from the internet, a white screen appears

launchUrl not working
import 'package:flutter/material.dart';
import 'package:haber_app/data/new_service.dart';
import 'package:haber_app/models/articles.dart';
import 'package:url_launcher/url_launcher.dart';
class Home extends StatefulWidget {
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Articles> articles = [];
#override
void initState() {
NewsService.getNews().then((value) {
setState(() {
articles = value!;
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Haberler'),
centerTitle: true,
),
body: Center(
child: ListView.builder(
itemCount: articles.length,
itemBuilder: (context, index) {
return Card(
child: Column(
children: [
Image.network(articles[index].urlToImage!),
ListTile(
leading: Icon(Icons.arrow_drop_down_circle),
title: Text(articles[index].title!),
subtitle: Text(articles[index].author!),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'açıklama açıklama açıklamaaçıklama açıklamaaçıklama'),
),
ButtonBar(
alignment: MainAxisAlignment.start,
children: [
ElevatedButton(
onPressed: () async {
await launchUrl(articles[index].url);
},
child: Text('Habere git'))
],
)
],
),
);
})),
);
}
}
Link of the code: https://github.com/ghedtoboss/haber_app
I am making a pull request, check the changes and merge. haber_app/pull/1
Changes are on
source.dart
class Source {
String? id;
String? name;
Source({this.id, this.name});
Source.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['id'] = id;
data['name'] = name;
return data;
}
}
news.dart
import 'articles.dart';
class News {
String? status;
int? totalResults;
List<Articles>? articles;
News({this.status, this.totalResults, this.articles});
News.fromJson(Map<String, dynamic> json) {
status = json['status'];
totalResults = json['totalResults'];
if (json['articles'] != null) {
articles = <Articles>[];
json['articles'].forEach((v) {
print(v);
articles!.add(Articles.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['status'] = status;
data['totalResults'] = totalResults;
if (articles != null) {
data['articles'] = articles!.map((v) => v.toJson()).toList();
}
return data;
}
}
NewsService
import 'package:haber_app/models/articles.dart';
import 'package:haber_app/models/news.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class NewsService {
static NewsService _singleton = NewsService._internal();
NewsService._internal();
factory NewsService() {
return _singleton;
}
static Future<List<Articles>?> getNews() async {
String api =
'https://newsapi.org/v2/top-headlines?country=tr&category=business&apiKey=0002834b74c04acd987883986ea38f96';
final Uri url = Uri.parse(api);
final response = await http.post(url);
if (response.body.isNotEmpty) {
final responseJson = json.decode(response.body);
News news = News.fromJson(responseJson);
return news.articles;
}
return null;
}
}
On body
import 'package:flutter/material.dart';
import 'package:haber_app/data/new_service.dart';
import 'package:haber_app/models/articles.dart';
import 'package:url_launcher/url_launcher.dart';
class Home extends StatefulWidget {
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
List<Articles> articles = [];
#override
void initState() {
super.initState();
loadData();
}
loadData() async {
NewsService.getNews().then((value) {
print(value);
setState(() {
articles = value ?? [];
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Haberler'),
centerTitle: true,
),
body: Center(
child: articles.isEmpty
? Text("loading or something")
: ListView.builder(
itemCount: articles.length,
itemBuilder: (context, index) {
return Card(
child: Column(
children: [
if (articles[index].urlToImage != null)
Image.network(articles[index].urlToImage!),
ListTile(
leading: Icon(Icons.arrow_drop_down_circle),
title: Text(articles[index].title ?? ""),
subtitle: Text(articles[index].author ?? ""),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'açıklama açıklama açıklamaaçıklama açıklamaaçıklama'),
),
ButtonBar(
alignment: MainAxisAlignment.start,
children: [
ElevatedButton(
onPressed: () async {
if (articles[index].url == null) {
return;
}
await launchUrl(
Uri.parse(articles[index].url!));
},
child: Text('Habere git'))
],
)
],
),
);
})),
);
}
}
You need to use Uri to launch
onPressed: () async {
if (articles[index].url == null) {
return;
}
await launchUrl(
Uri.parse(articles[index].url!));
},
Also make sure to set up the configurationsection
More about url_launcher

Flutter "A non-null String must be provided to a Text widget."

I am trying to get the data from an API and I get the data, but when I show it on the screen I get this error
Assertion failed: file:///home/builder/hotbuilder/packages/flutter/lib/src/widgets/text.dart:378:10
data != null.
I saw some examples, but I still don't understand how to solve it
This is main.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:flutter_api_git/modelo/repo.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter Demo',
home: Home(),
);
}
}
Future<All> fetchRepos() async {
final response =
await http.get(Uri.parse('https://api.github.com/users/IcaroOli/repos'));
if (response.statusCode == 200) {
print(response.body);
return All.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to fetch repos!');
}
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
late Future<All> futureRepo;
#override
void initState() {
super.initState();
futureRepo = fetchRepos();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GitHub API!'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: FutureBuilder<All>(
future: futureRepo,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Repo> repos = <Repo>[];
for (int i = 0; i < snapshot.data!.repos.length; i++) {
repos.add(
Repo(
name: snapshot.data.repos[i].name,
description: snapshot.data!.repos[i].description,
htmlUrl: snapshot.data!.repos[i].htmlUrl,
stargazersCount: snapshot.data!.repos[i].stargazersCount,
),
);
}
return ListView(
children: repos
.map(
(r) => Card(
color: Colors.blue[300],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
r.name,
style: const TextStyle(fontSize: 30.0),
),
Text(r.stargazersCount.toString()),
],
),
Text(
r.description,
style: const TextStyle(fontSize: 23.0),
),
Text(r.htmlUrl),
],
),
),
),
)
.toList(),
);
} else if (snapshot.hasError) {
return const Center(
child: Text('Error!'),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
),
);
}
}
This is repo.dart
class Repo {
String name;
String htmlUrl; // hmtl_url
int stargazersCount; //stargazers_count
String description;
Repo(
{required this.name,
required this.htmlUrl,
required this.stargazersCount,
required this.description});
factory Repo.fromJson(Map<String, dynamic> json) {
return Repo(
name: json['name'],
htmlUrl: json['html_url'],
stargazersCount: json['stargazers_count'],
description: json['description'],
);
}
}
class All {
List<Repo> repos;
All({required this.repos});
factory All.fromJson(List<dynamic> json) {
List<Repo> repos = <Repo>[];
repos = json.map((r) => Repo.fromJson(r)).toList();
return All(repos: repos);
}
}
class Repo {
String name;
String htmlUrl; // hmtl_url
int stargazersCount; //stargazers_count
String description;
Repo(
{required this.name,
required this.htmlUrl,
required this.stargazersCount,
required this.description});
factory Repo.fromJson(Map<String, dynamic> json) {
return Repo(
name: json['name'],
htmlUrl: json['html_url'],
stargazersCount: json['stargazers_count'],
description: json['description'],
);
}
}
class All {
List<Repo> repos;
All({required this.repos});
factory All.fromJson(List<dynamic> json) {
List<Repo> repos = <Repo>[];
repos = json.map((r) => Repo.fromJson(r)).toList();
return All(repos: repos);
}
}
This happens when you pass a null value to a text widget.
Try adding empty string if the value is null.
Text(r.name ?? ""),
One way to make sure they are never null is to provide fallback values in the factory of Repo, for example an empty string, like this:
factory Repo.fromJson(Map<String, dynamic> json) {
return Repo(
name: json['name'] ?? "",
htmlUrl: json['html_url'] ?? "",
stargazersCount: json['stargazers_count'] ?? 0,
description: json['description'] ?? "",
);
}

Flutter Redux and Hook. How to watch variable instance changing inside useEffect like react?

Im new in flutter and trying to watch a variable "count "that is in state of reducer like React native. I made my redux and hook work perfeclty. The variable count change in the screen but never call useEffect again(only once) event if i change de action. Tried diferente aproachs without success and tried to put StoreProvider.of(context).state.balanceState.count directly in the dependecis of useEffect. If i use useState works like should.
class StatePage extends HookWidget {
const StatePage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
int count = StoreProvider.of<AppState>(context).state.balanceState.count;
useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((_) =>
StoreProvider.of<AppState>(context).dispatch(getBalanceThunk()));
}, [count]);
return Scaffold(
appBar: AppBar(
title: Text('ola'),
),
body: Center(
child: StoreConnector<AppState, BalanceState>(
converter: (store) => store.state.balanceState,
builder: (context, state) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (state.loading)
Column(
children: [
CircularProgressIndicator(),
SizedBox(
height: 30,
)
],
),
Text(state.message),
Text(count.toString()),
RaisedButton(
child: Text('Clicar'),
onPressed: (){
StoreProvider.of<AppState>(context).dispatch(SetCountAction(count++));
})
],
);
},
),
),
);
}
}
my Action
abstract class BalanceAction {}
class BalanceStateAction extends BalanceAction {
final bool loading;
final bool error;
final String message;
BalanceStateAction(this.loading, this.error, this.message);
}
class SetBalanceAction extends BalanceAction {
final double value;
SetBalanceAction(this.value);
}
class SetCountAction extends BalanceAction {
final int count;
SetCountAction(this.count);
}
my Reducer
import 'package:flutter_red_test/redux/actions/balance_action.dart';
class BalanceState {
final bool loading;
final bool error;
final String message;
final double value;
final int count;
BalanceState({
this.loading = false,
this.count = 0,
this.error = false,
this.message = '',
this.value = null,
});
factory BalanceState.initialState(){
return BalanceState(
loading: false,
error: false,
message: '',
value: null,
count: 0,
);
}
BalanceState copy({bool loading, bool error, String message, double value, int count}){
return BalanceState(
loading: loading ?? this.loading,
error: error ?? this.error,
message: message ?? this.message,
value: value ?? this.value,
count: count ?? this.count,
);
}
}
BalanceState balanceReducer(BalanceState state, BalanceAction action){
if(action is BalanceStateAction){
return state.copy(
loading: action.loading,
message: action.message,
value: state.value,
error: action.error,
count: state.count,
);
}
if(action is SetBalanceAction){
return state.copy(
loading: false,
message: 'Seu saldo é de 20.000',
value: action.value,
count: state.count,
error: false,
);
}
if(action is SetCountAction){
return state.copy(
loading: false,
message: state.message,
value: state.value,
count: action.count,
error: false,
);
}
return state;
}