Skip to content

Instantly share code, notes, and snippets.

@alash3al
Created March 24, 2022 12:34
Show Gist options
  • Save alash3al/c7e0b34a0f00b603b19d743ddb902917 to your computer and use it in GitHub Desktop.
Save alash3al/c7e0b34a0f00b603b19d743ddb902917 to your computer and use it in GitHub Desktop.
elnews.app ui spaghetti code
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