Created
March 24, 2022 12:34
-
-
Save alash3al/c7e0b34a0f00b603b19d743ddb902917 to your computer and use it in GitHub Desktop.
elnews.app ui spaghetti code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:async_builder/async_builder.dart'; | |
import 'package:dio/dio.dart'; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:get/get.dart'; | |
import 'package:google_fonts/google_fonts.dart'; | |
import 'package:responsive_framework/responsive_framework.dart'; | |
import 'package:url_launcher/url_launcher.dart'; | |
Dio dio = Dio( | |
BaseOptions( | |
baseUrl: const String.fromEnvironment( | |
"API_BASE_URL", | |
defaultValue: "http://localhost:8080/v1", | |
), | |
), | |
); | |
var categoryIcons = { | |
'news/health': const Icon(Icons.health_and_safety), | |
'news/health/covid': const Icon(Icons.coronavirus_outlined), | |
'egypt/news/trending': const Icon(Icons.trending_up), | |
'news/world': const Icon(Icons.explore), | |
'egypt/news': const Icon(Icons.adjust_sharp), | |
'news/sports': const Icon(Icons.sports_baseball_outlined), | |
}; | |
class Messages extends Translations { | |
@override | |
Map<String, Map<String, String>> get keys => { | |
'en_US': { | |
'aed': 'Emirati Dirham', | |
'aud': 'Australian Dollar', | |
'cad': 'Canadian Dollars', | |
'chf': 'Swiss Francs', | |
'cny': 'Chinese Yuan', | |
'eur': 'Euro', | |
'gbp': 'British Pound Sterling', | |
'hkd': 'Hong Kong Dollar', | |
'inr': 'Indian Rupee', | |
'jpy': 'Japanese Yen', | |
'nzd': 'New Zealand Dollar', | |
'try': 'Turkish Lira', | |
'usd': 'United States Dollar', | |
'zar': 'South African Rand', | |
'gold-karat-24': 'Gold karat 24' | |
}, | |
'ar_EG': { | |
'aed': 'الدرهم الإماراتي', | |
'aud': 'الدولار الأسترالي', | |
'cad': 'الدولار الكندي', | |
'chf': 'الفرانك السويسري', | |
'cny': 'اليوان الصيني', | |
'eur': 'اليورو', | |
'gbp': 'الجنيه الإسترليني', | |
'hkd': 'الدولار الهونج كونجي', | |
'inr': 'الروبيه الهنديه', | |
'jpy': 'الين اليباني', | |
'nzd': 'الدولار النيوزلاندي', | |
'try': 'الليرا التركيه', | |
'usd': 'الدولار الأمريكي', | |
'zar': 'الدولار الجنوب أفريقي', | |
'gold-karat-24': 'جرام الذهب عيار 24', | |
'gold-karat-21': 'جرام الذهب عيار 21', | |
'gold-karat-18': 'جرام الذهب عيار 18', | |
'gold-karat-14': 'جرام الذهب عيار 14', | |
'gold-karat-10': 'جرام الذهب عيار 10', | |
'E£': 'ج.م', | |
'Trending News': 'الأخبار الرائجة', | |
'Currency Rates': 'أسعار العملات', | |
'ELNEWS': 'الأخبار', | |
'Prices & News are updated in near real-time': | |
'الأسعار والأخبار يتم تحديثها على مدار الساعه لحظه بلحظه', | |
'News Headlines': 'عناوين الأخبار', | |
'seconds': 'ثواني', | |
'minutes': 'دقائق', | |
'hours': 'ساعات', | |
'days': 'ايام', | |
'ago': 'مضت', | |
'since': 'منذ', | |
} | |
}; | |
} | |
class When extends StatelessWidget { | |
final bool booleanExpr; | |
final Widget thenChild; | |
final Widget? elseChild; | |
const When({ | |
Key? key, | |
required this.booleanExpr, | |
required this.thenChild, | |
required this.elseChild, | |
}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
if (booleanExpr) { | |
return thenChild; | |
} | |
return elseChild ?? const SizedBox.shrink(); | |
} | |
} | |
void main() { | |
LicenseRegistry.addLicense(() async* { | |
final license = await rootBundle.loadString('google_fonts/OFL.txt'); | |
yield LicenseEntryWithLineBreaks(['google_fonts'], license); | |
}); | |
runApp(const Elnews()); | |
} | |
class Elnews extends StatelessWidget { | |
const Elnews({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return GetMaterialApp( | |
translations: Messages(), | |
title: 'ELNEWS - Realtime News, Trends & Currencies Rates', | |
locale: const Locale('ar', 'EG'), | |
fallbackLocale: const Locale('en', 'US'), | |
theme: ThemeData( | |
primarySwatch: Colors.indigo, | |
textTheme: GoogleFonts.cairoTextTheme(Theme.of(context).textTheme), | |
), | |
builder: (context, widget) => ResponsiveWrapper.builder(widget, | |
maxWidth: 900, | |
minWidth: 480, | |
defaultScale: true, | |
breakpoints: [ | |
const ResponsiveBreakpoint.resize(480, name: MOBILE), | |
const ResponsiveBreakpoint.autoScale(800, name: TABLET), | |
const ResponsiveBreakpoint.resize(1000, name: DESKTOP), | |
], | |
background: Container(color: const Color(0xFFF5F5F5))), | |
home: Home(), | |
); | |
} | |
} | |
class Home extends StatelessWidget { | |
final refresh = "".obs; | |
Home({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
Future.delayed(const Duration(seconds: 1), () { | |
ScaffoldMessenger.of(context).showSnackBar(SnackBar( | |
content: Text( | |
"Prices & News are updated in near real-time".tr, | |
style: GoogleFonts.getFont("Cairo"), | |
), | |
)); | |
}); | |
return Obx(() { | |
var _ = refresh.value; | |
return DefaultTabController( | |
length: 2, | |
child: Scaffold( | |
appBar: AppBar( | |
title: Text( | |
"ELNEWS".tr, | |
style: const TextStyle( | |
fontWeight: FontWeight.w900, | |
), | |
), | |
actions: [ | |
IconButton( | |
onPressed: () { | |
refresh(DateTime.now().toString()); | |
}, | |
icon: const Icon(Icons.refresh), | |
), | |
], | |
centerTitle: true, | |
bottom: TabBar( | |
tabs: [ | |
Padding( | |
padding: const EdgeInsets.all(10.0), | |
child: Text( | |
"News Headlines".tr, | |
style: GoogleFonts.getFont("Cairo"), | |
), | |
), | |
Padding( | |
padding: const EdgeInsets.all(10.0), | |
child: Text( | |
"Currency Rates".tr, | |
style: GoogleFonts.getFont("Cairo"), | |
), | |
), | |
], | |
), | |
), | |
body: TabBarView( | |
children: [ | |
AsyncBuilder<List<NewsItemCard>>( | |
future: NewsItemCard.load(), | |
waiting: (c) => Transform.scale( | |
child: const CircularProgressIndicator(), | |
scale: 0.2, | |
), | |
builder: (context, value) { | |
return ListView( | |
children: value ?? [], | |
); | |
}, | |
), | |
AsyncBuilder<List<PriceItemCard>>( | |
future: PriceItemCard.load(), | |
waiting: (c) => Transform.scale( | |
child: const CircularProgressIndicator(), | |
scale: 0.2, | |
), | |
builder: (context, value) { | |
return ListView( | |
children: value ?? [], | |
); | |
}, | |
), | |
], | |
), | |
), | |
); | |
}); | |
} | |
} | |
class PriceItemCard extends StatelessWidget { | |
final String currency; | |
final double currentPrice; | |
final double? previousPrice; | |
final double sinceSeconds; | |
const PriceItemCard({ | |
Key? key, | |
required this.currency, | |
required this.currentPrice, | |
required this.previousPrice, | |
required this.sinceSeconds, | |
}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
var clr = Colors.green; | |
if (currentPrice < (previousPrice ?? 0)) { | |
clr = Colors.red; | |
} | |
var sinceUnit = ""; | |
var sinceValue = 0; | |
var timeDiff = Duration(seconds: sinceSeconds.toInt()); | |
if (timeDiff.inSeconds < 60) { | |
sinceUnit = "seconds"; | |
sinceValue = timeDiff.inSeconds; | |
} else if (timeDiff.inMinutes < 60) { | |
sinceUnit = "minutes"; | |
sinceValue = timeDiff.inMinutes; | |
} else if (timeDiff.inHours < 24) { | |
sinceUnit = "hours"; | |
sinceValue = timeDiff.inHours; | |
} else { | |
sinceUnit = "days"; | |
sinceValue = timeDiff.inDays; | |
} | |
return ListTile( | |
leading: When( | |
booleanExpr: currency.startsWith('gold-karat'), | |
thenChild: CircleAvatar( | |
backgroundColor: Colors.yellow[300], | |
), | |
elseChild: CircleAvatar( | |
backgroundImage: AssetImage( | |
'icons/currency/$currency.png', | |
package: 'currency_icons', | |
), | |
), | |
), | |
title: Text( | |
currency.tr, | |
style: const TextStyle( | |
fontWeight: FontWeight.w900, | |
), | |
), | |
subtitle: Text( | |
"${'since'.tr} ${sinceValue.toString()} ${sinceUnit.tr} ${'ago'.tr}", | |
), | |
trailing: Text( | |
currentPrice.toStringAsFixed(2) + " " + "E£".tr, | |
style: TextStyle( | |
fontWeight: FontWeight.w900, | |
color: Color(clr.value), | |
), | |
), | |
shape: RoundedRectangleBorder( | |
side: BorderSide( | |
color: Colors.grey[200]!, | |
width: 1.0, | |
), | |
), | |
); | |
} | |
// we're using "http://goldpricez.com/eg/21k/gram" as reference to | |
// convert between gold karats. | |
static Future<List<PriceItemCard>> load() async { | |
var items = List.from((await dio.get("/prices")).data); | |
var karat24Item = items.firstWhere((element) { | |
return element["product_key"] == "gold-karat-24"; | |
}); | |
var itemsSorted = [ | |
karat24Item, | |
(() { | |
var item = Map.from(karat24Item); | |
item["product_key"] = "gold-karat-21"; | |
item["current_price"] = item["current_price"] * 0.88; | |
item["previous_price"] = item["previous_price"] * 0.88; | |
return item; | |
})(), | |
(() { | |
var item = Map.from(karat24Item); | |
item["product_key"] = "gold-karat-18"; | |
item["current_price"] = item["current_price"] * 0.75; | |
item["previous_price"] = item["previous_price"] * 0.75; | |
return item; | |
})(), | |
(() { | |
var item = Map.from(karat24Item); | |
item["product_key"] = "gold-karat-14"; | |
item["current_price"] = item["current_price"] * 0.58; | |
item["previous_price"] = item["previous_price"] * 0.58; | |
return item; | |
})(), | |
(() { | |
var item = Map.from(karat24Item); | |
item["product_key"] = "gold-karat-10"; | |
item["current_price"] = item["current_price"] * 0.42; | |
item["previous_price"] = item["previous_price"] * 0.42; | |
return item; | |
})(), | |
items.firstWhere((element) => element["product_key"] == "usd"), | |
items.firstWhere((element) => element["product_key"] == "eur"), | |
items.firstWhere((element) => element["product_key"] == "gbp"), | |
items.firstWhere((element) => element["product_key"] == "aed"), | |
items.firstWhere((element) => element["product_key"] == "try"), | |
items.firstWhere((element) => element["product_key"] == "cny"), | |
items.firstWhere((element) => element["product_key"] == "cad"), | |
items.firstWhere((element) => element["product_key"] == "jpy"), | |
items.firstWhere((element) => element["product_key"] == "inr"), | |
items.firstWhere((element) => element["product_key"] == "aud"), | |
items.firstWhere((element) => element["product_key"] == "chf"), | |
items.firstWhere((element) => element["product_key"] == "hkd"), | |
items.firstWhere((element) => element["product_key"] == "nzd"), | |
items.firstWhere((element) => element["product_key"] == "zar"), | |
]; | |
return Future.value( | |
List<PriceItemCard>.from( | |
itemsSorted.map((item) { | |
var priceItem = Map<String, dynamic>.from(item); | |
return PriceItemCard( | |
currency: priceItem["product_key"], | |
currentPrice: priceItem["current_price"], | |
previousPrice: priceItem["previous_price"] ?? 0.0, | |
sinceSeconds: priceItem["since_seconds"], | |
); | |
}), | |
), | |
); | |
} | |
} | |
class NewsItemCard extends StatelessWidget { | |
final String title; | |
final String url; | |
final String category; | |
final DateTime time; | |
final double sinceSeconds; | |
final String? imageURL; | |
final String? description; | |
final String? websiteName; | |
const NewsItemCard({ | |
Key? key, | |
required this.title, | |
required this.description, | |
required this.url, | |
required this.category, | |
required this.websiteName, | |
required this.imageURL, | |
required this.time, | |
required this.sinceSeconds, | |
}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
var sinceUnit = ""; | |
var sinceValue = 0; | |
var timeDiff = Duration(seconds: sinceSeconds.toInt()); | |
if (timeDiff.inSeconds < 60) { | |
sinceUnit = "seconds"; | |
sinceValue = timeDiff.inSeconds; | |
} else if (timeDiff.inMinutes < 60) { | |
sinceUnit = "minutes"; | |
sinceValue = timeDiff.inMinutes; | |
} else if (timeDiff.inHours < 24) { | |
sinceUnit = "hours"; | |
sinceValue = timeDiff.inHours; | |
} else { | |
sinceUnit = "days"; | |
sinceValue = timeDiff.inDays; | |
} | |
return ListTile( | |
leading: categoryIcons[category], | |
title: Text( | |
title, | |
style: const TextStyle( | |
fontWeight: FontWeight.w700, | |
), | |
), | |
subtitle: Text( | |
"${'since'.tr} ${sinceValue.toString()} ${sinceUnit.tr} ${'ago'.tr}", | |
), | |
trailing: const Icon( | |
Icons.arrow_back_ios_new, | |
color: Colors.indigo, | |
), | |
onTap: () async { | |
await launch(url); | |
}, | |
shape: RoundedRectangleBorder( | |
side: BorderSide( | |
color: Colors.grey[200]!, | |
width: 1.0, | |
), | |
), | |
); | |
} | |
static Future<List<NewsItemCard>> load() async { | |
var items = List.from((await dio.get("/news")).data); | |
return Future.value( | |
List<NewsItemCard>.from( | |
items.map((item) { | |
var newsItem = Map<String, dynamic>.from(item); | |
return NewsItemCard( | |
title: newsItem["title"], | |
imageURL: newsItem["image_url"], | |
category: newsItem["category"], | |
description: newsItem["description"], | |
url: newsItem["article_url"], | |
sinceSeconds: newsItem["since_seconds"], | |
websiteName: newsItem["website_name"] ?? 0.0, | |
time: DateTime.parse(newsItem["time"]), | |
); | |
}), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment