Adding new features

This commit is contained in:
Erfan Rahmati
2022-07-08 13:43:09 +04:30
parent 97f060af66
commit f69429d50b
16 changed files with 542 additions and 160 deletions

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hive_flutter/adapters.dart';
@ -33,7 +34,7 @@ void main() async {
Get.put(SearchBarController());
Get.put(CacheData());
runApp(const App());
runApp(const ProviderScope(child: App()));
}
class App extends StatelessWidget {

View File

@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:movielab/models/hive/models/show_preview.dart';
import 'package:movielab/models/show_models/show_preview_model.dart';
@ -43,7 +44,10 @@ HiveShowPreview convertShowPreviewToHive(ShowPreview showPreview) {
}
Future<HiveShowPreview> convertFullShowToHive(
FullShow fullShow, String rank) async {
{required FullShow fullShow,
required String rank,
DateTime? date,
TimeOfDay? time}) async {
String crew = "";
await getShowCrew(fullShow: fullShow).then((value) => crew = value);
return HiveShowPreview()
@ -61,7 +65,9 @@ Future<HiveShowPreview> convertFullShowToHive(
..domestic = fullShow.domestic
..domesticLifetimeGross = fullShow.domesticLifetimeGross
..foreign = fullShow.foreign
..foreignLifetimeGross = fullShow.foreignLifetimeGross;
..foreignLifetimeGross = fullShow.foreignLifetimeGross
..watchDate = date
..watchTime = time;
}
Future<String> getShowCrew({required FullShow fullShow}) async {

View File

@ -14,4 +14,6 @@ class ShowPreviewFields {
static const int domestic = 12;
static const int foreignLifetimeGross = 13;
static const int foreign = 14;
static const int watchDate = 15;
static const int watchTime = 16;
}

View File

@ -1,3 +1,4 @@
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import '../hive_helper/fields/show_preview_fields.dart';
import '../hive_helper/hive_adapters.dart';
@ -37,4 +38,8 @@ class HiveShowPreview extends HiveObject {
late String foreignLifetimeGross;
@HiveField(ShowPreviewFields.foreign)
late String foreign;
@HiveField(ShowPreviewFields.watchDate)
late DateTime? watchDate;
@HiveField(ShowPreviewFields.watchTime)
late TimeOfDay? watchTime;
}

View File

@ -1,5 +1,7 @@
import 'dart:convert';
import 'package:flutter/material.dart';
// Movie or TV show preview model class
class ShowPreview {
final String id;
@ -17,6 +19,8 @@ class ShowPreview {
final String domestic;
final String foreignLifetimeGross;
final String foreign;
final DateTime? watchDate;
final TimeOfDay? watchTime;
const ShowPreview({
required this.id,
@ -34,6 +38,8 @@ class ShowPreview {
required this.domestic,
required this.foreignLifetimeGross,
required this.foreign,
this.watchDate,
this.watchTime,
});
factory ShowPreview.fromJson(Map<String, dynamic> json) {

View File

@ -0,0 +1,5 @@
extension StringExtension on String {
String capitalize() {
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
}
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hive_flutter/adapters.dart';
import '../models/hive/convertor.dart';
import '../models/hive/models/show_preview.dart';
@ -16,10 +17,16 @@ class PreferencesShareholder {
// Add an item to a list in the shared preferences
Future<bool> addShowToList(
{required FullShow fullShow, required String listName}) async {
{required FullShow fullShow,
required String listName,
DateTime? date,
TimeOfDay? time}) async {
Box<HiveShowPreview> list = Hive.box<HiveShowPreview>(listName);
HiveShowPreview hiveShow =
await convertFullShowToHive(fullShow, (list.length + 1).toString());
HiveShowPreview hiveShow = await convertFullShowToHive(
fullShow: fullShow,
rank: (list.length + 1).toString(),
date: date,
time: time);
list.put(list.length + 1, hiveShow);
if (kDebugMode) {
print("The item added to $listName");
@ -48,17 +55,21 @@ class PreferencesShareholder {
List<String> listNames = ["collection", "watchlist", "history"];
Map<String, bool> result = {};
for (String listName in listNames) {
Box<HiveShowPreview> collection = Hive.box<HiveShowPreview>(listName);
for (int i = 0; i < collection.length; i++) {
if (collection.getAt(i)?.id == showId) {
Box<HiveShowPreview> list = Hive.box<HiveShowPreview>(listName);
for (int i = 0; i < list.length; i++) {
if (list.getAt(i)?.id == showId) {
if (kDebugMode) {
print("Item is in $listName");
}
result[listName] = true;
break;
} else {
result[listName] = false;
}
}
result[listName] = false;
if (result[listName] != true) {
result[listName] = false;
}
}
return result;
}

View File

@ -1,12 +1,40 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../../../../../constants/colors.dart';
import '../../../../../models/show_models/full_show_model.dart';
import '../../../../../modules/preferences_shareholder.dart';
import 'sections/lists_info/lists_info.dart';
class ShowPageBottonBar extends StatelessWidget {
const ShowPageBottonBar({Key? key}) : super(key: key);
class ShowPageBottonBar extends StatefulWidget {
final FullShow show;
final Map<String, bool> isThereInLists;
const ShowPageBottonBar(
{Key? key, required this.show, required this.isThereInLists})
: super(key: key);
@override
State<ShowPageBottonBar> createState() => _ShowPageBottonBarState();
}
class _ShowPageBottonBarState extends State<ShowPageBottonBar>
with TickerProviderStateMixin {
final PreferencesShareholder _preferencesShareholder =
PreferencesShareholder();
late FToast fToast;
@override
void initState() {
super.initState();
fToast = FToast();
fToast.init(context);
}
@override
Widget build(BuildContext context) {
if (kDebugMode) {
print(widget.isThereInLists);
}
return SizedBox(
height: 60,
child: Row(
@ -34,23 +62,12 @@ class ShowPageBottonBar extends StatelessWidget {
)),
clipBehavior: Clip.antiAliasWithSaveLayer,
backgroundColor: kSecondaryColor,
transitionAnimationController: AnimationController(
duration: const Duration(milliseconds: 235), vsync: this),
builder: (context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 20),
height: 235,
child: Column(
children: [
button(context,
icon: FontAwesomeIcons.circle,
text: 'Mark as watched'),
button(context,
icon: FontAwesomeIcons.bookmark,
text: 'Add to watchlist'),
button(context,
icon: FontAwesomeIcons.rectangleList,
text: 'Add to collection'),
],
),
return ShowPageListsInfo(
show: widget.show,
isThereInLists: widget.isThereInLists,
);
},
);
@ -108,44 +125,4 @@ class ShowPageBottonBar extends StatelessWidget {
),
);
}
Widget button(BuildContext context,
{required String text,
required IconData icon,
EdgeInsets margin = const EdgeInsets.symmetric(vertical: 7.5)}) {
return Container(
height: 50,
margin: margin,
width: MediaQuery.of(context).size.width - 100,
decoration: BoxDecoration(
color: const Color(0xff2a425f).withOpacity(0.75),
borderRadius: BorderRadius.circular(15)),
child: TextButton(
onPressed: () {},
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
),
child: Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30),
child: Icon(
icon,
color: Colors.white,
size: 20,
),
),
Text(
text,
style: const TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.w800),
),
],
)),
);
}
}

View File

@ -0,0 +1,142 @@
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:movielab/modules/capitalizer.dart';
import '../../../../../../../constants/colors.dart';
import '../../../../../../../models/show_models/full_show_model.dart';
import '../../../../../../../modules/preferences_shareholder.dart';
import '../../../../../../../widgets/buttons/activeable_button.dart';
import '../../../../../../../widgets/toast.dart';
import '../../watchtime.dart';
class ShowPageListsInfo extends StatefulWidget {
final FullShow show;
final Map<String, bool> isThereInLists;
const ShowPageListsInfo(
{Key? key, required this.show, required this.isThereInLists})
: super(key: key);
@override
State<ShowPageListsInfo> createState() => _ShowPageListsInfoState();
}
class _ShowPageListsInfoState extends State<ShowPageListsInfo>
with TickerProviderStateMixin {
final PreferencesShareholder _preferencesShareholder =
PreferencesShareholder();
late FToast fToast;
@override
void initState() {
super.initState();
fToast = FToast();
fToast.init(context);
}
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 20),
height: 235,
child: Column(
children: [
ActiveableButton(
icon: FontAwesomeIcons.circle,
activeIcon: FontAwesomeIcons.solidCircle,
text: 'Mark as watched',
activeText: 'Watched',
activeColor: kPrimaryColor,
isActive: widget.isThereInLists['history'] ?? false,
onTap: () async {
if (widget.isThereInLists["history"] == false) {
Navigator.pop(context);
await Future.delayed(const Duration(milliseconds: 200));
showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(
top: Radius.circular(20),
)),
clipBehavior: Clip.antiAliasWithSaveLayer,
backgroundColor: kSecondaryColor,
transitionAnimationController: AnimationController(
duration: const Duration(milliseconds: 225),
vsync: this),
builder: (context) {
return ShowPageAddWatchDate(
show: widget.show,
);
});
} else {
handleOnTap(listName: "history");
}
}),
ActiveableButton(
icon: FontAwesomeIcons.bookmark,
activeIcon: FontAwesomeIcons.solidBookmark,
text: 'Add to watchlist',
activeText: 'Listed on watchlist',
activeColor: kAccentColor,
isActive: widget.isThereInLists['watchlist'] ?? false,
onTap: () {
handleOnTap(listName: "watchlist");
}),
ActiveableButton(
icon: FontAwesomeIcons.rectangleList,
activeIcon: FontAwesomeIcons.bookBookmark,
text: 'Add to collection',
activeText: 'Collected',
activeColor: kImdbColor,
isActive: widget.isThereInLists['collection'] ?? false,
onTap: () {
handleOnTap(listName: "collection");
}),
],
),
);
}
void handleOnTap({
required String listName,
}) async {
if (widget.isThereInLists[listName] == false) {
_preferencesShareholder.addShowToList(
fullShow: widget.show, listName: listName);
setState(() {
widget.isThereInLists[listName] = true;
});
await Future.delayed(const Duration(milliseconds: 200));
// ignore: use_build_context_synchronously
Navigator.pop(context);
await Future.delayed(const Duration(milliseconds: 200));
fToast.showToast(
child: ToastWidget(
mainText: "Saved to ${listName.capitalize()}",
buttonText: "See list",
buttonColor: kAccentColor,
buttonOnTap: () {},
),
gravity: ToastGravity.BOTTOM,
toastDuration: const Duration(seconds: 3),
);
} else {
_preferencesShareholder.deleteFromList(
showId: widget.show.id, listName: listName);
setState(() {
widget.isThereInLists[listName] = false;
});
await Future.delayed(const Duration(milliseconds: 200));
// ignore: use_build_context_synchronously
Navigator.pop(context);
await Future.delayed(const Duration(milliseconds: 200));
fToast.showToast(
child: ToastWidget(
mainText: "Ramoved from ${listName.capitalize()}",
buttonText: "Undo",
buttonColor: kPrimaryColor,
buttonOnTap: () {}),
gravity: ToastGravity.BOTTOM,
toastDuration: const Duration(seconds: 3),
);
}
}
}

View File

@ -1,101 +1,57 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:movielab/constants/colors.dart';
import 'package:movielab/models/show_models/full_show_model.dart';
import 'package:movielab/widgets/buttons/glassmorphism_button.dart';
import '../../../../../modules/preferences_shareholder.dart';
import '../../../../../widgets/toast.dart';
// ignore: must_be_immutable
class ShowPageAddWatchTime extends StatefulWidget {
class ShowPageAddWatchDate extends ConsumerStatefulWidget {
final FullShow show;
const ShowPageAddWatchTime({Key? key, required this.show}) : super(key: key);
const ShowPageAddWatchDate({Key? key, required this.show}) : super(key: key);
@override
State<ShowPageAddWatchTime> createState() => _ShowPageAddWatchTimeState();
ShowPageAddWatchDateState createState() => ShowPageAddWatchDateState();
}
class _ShowPageAddWatchTimeState extends State<ShowPageAddWatchTime> {
bool isOtherDateSectionOpen = false;
bool showDateSelector = false;
DateTime selectedDate = DateTime.now();
TimeOfDay selectedTime = TimeOfDay.now();
List<String> months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
class ShowPageAddWatchDateState extends ConsumerState<ShowPageAddWatchDate> {
final PreferencesShareholder _preferencesShareholder =
PreferencesShareholder();
late bool isOtherDateSectionOpen, showDateSelector;
late TimeOfDay selectedTime;
late DateTime selectedDate;
late List<String> months;
late FToast fToast;
Future<void> _selectDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
builder: (BuildContext context, child) {
return Theme(
data: Theme.of(context).copyWith(
dialogBackgroundColor: kSecondaryColor,
primaryColor: kPrimaryColor,
colorScheme: const ColorScheme.light(
primary: kPrimaryColor,
onPrimary: Colors.white,
onSurface: Colors.white,
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
primary: kPrimaryColor,
),
),
),
child: child!);
},
initialDate: selectedDate,
currentDate: DateTime.now(),
selectableDayPredicate: (DateTime date) =>
date.isAfter(DateTime.now()) ? false : true,
firstDate: DateTime(1901),
lastDate: DateTime(2101));
if (picked != null && picked != selectedDate) {
setState(() {
selectedDate = picked;
print(selectedDate);
});
}
}
@override
void initState() {
super.initState();
isOtherDateSectionOpen = false;
showDateSelector = false;
Future<void> _selectTime(BuildContext context) async {
final TimeOfDay? timeOfDay = await showTimePicker(
context: context,
builder: (BuildContext context, child) {
return Theme(
data: ThemeData.dark().copyWith(
primaryColor: kPrimaryColor,
timePickerTheme: const TimePickerThemeData(
backgroundColor: kSecondaryColor,
dialTextColor: Colors.white,
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
primary: Colors.white,
),
),
),
child: child!);
},
initialTime: selectedTime,
initialEntryMode: TimePickerEntryMode.dial,
);
if (timeOfDay != null && timeOfDay != selectedTime) {
setState(() {
selectedTime = timeOfDay;
});
}
selectedTime = TimeOfDay.now();
selectedDate = DateTime.now();
months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
];
fToast = FToast();
fToast.init(context);
}
@override
@ -121,7 +77,9 @@ class _ShowPageAddWatchTimeState extends State<ShowPageAddWatchTime> {
],
),
TextButton(
onPressed: () {},
onPressed: () {
markAsWatched(date: DateTime.now(), time: TimeOfDay.now());
},
style: TextButton.styleFrom(
padding:
const EdgeInsets.symmetric(horizontal: 25, vertical: 10)),
@ -145,7 +103,11 @@ class _ShowPageAddWatchTimeState extends State<ShowPageAddWatchTime> {
),
),
TextButton(
onPressed: () {},
onPressed: () {
markAsWatched(
date: DateTime.parse(widget.show.releaseDate),
time: TimeOfDay.fromDateTime(DateTime.parse("00:00")));
},
style: TextButton.styleFrom(
padding:
const EdgeInsets.symmetric(horizontal: 25, vertical: 10)),
@ -332,4 +294,86 @@ class _ShowPageAddWatchTimeState extends State<ShowPageAddWatchTime> {
),
);
}
Future<void> _selectDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
builder: (BuildContext context, child) {
return Theme(
data: Theme.of(context).copyWith(
dialogBackgroundColor: kSecondaryColor,
primaryColor: kPrimaryColor,
colorScheme: const ColorScheme.light(
primary: kPrimaryColor,
onPrimary: Colors.white,
onSurface: Colors.white,
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
primary: kPrimaryColor,
),
),
),
child: child!);
},
initialDate: selectedDate,
currentDate: DateTime.now(),
selectableDayPredicate: (DateTime date) =>
date.isAfter(DateTime.now()) ? false : true,
firstDate: DateTime(1901),
lastDate: DateTime(2101));
if (picked != null && picked != selectedDate) {
setState(() {
selectedDate = picked;
});
}
}
Future<void> _selectTime(BuildContext context) async {
final TimeOfDay? timeOfDay = await showTimePicker(
context: context,
builder: (BuildContext context, child) {
return Theme(
data: ThemeData.dark().copyWith(
primaryColor: kPrimaryColor,
timePickerTheme: const TimePickerThemeData(
backgroundColor: kSecondaryColor,
dialTextColor: Colors.white,
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
primary: Colors.white,
),
),
),
child: child!);
},
initialTime: selectedTime,
initialEntryMode: TimePickerEntryMode.dial,
);
if (timeOfDay != null && timeOfDay != selectedTime) {
setState(() {
selectedTime = timeOfDay;
});
}
}
markAsWatched({required DateTime date, required TimeOfDay time}) async {
_preferencesShareholder.addShowToList(
fullShow: widget.show, listName: "history", date: date, time: time);
await Future.delayed(const Duration(milliseconds: 200));
// ignore: use_build_context_synchronously
Navigator.pop(context);
await Future.delayed(const Duration(milliseconds: 200));
fToast.showToast(
child: ToastWidget(
mainText: "Saved to History}",
buttonText: "See list",
buttonColor: kAccentColor,
buttonOnTap: () {},
),
gravity: ToastGravity.BOTTOM,
toastDuration: const Duration(seconds: 3),
);
}
}

View File

@ -12,7 +12,7 @@ import '../../../modules/preferences_shareholder.dart';
import 'get_show_info.dart';
import 'sections/bottom_bar/bottom_bar.dart';
import 'sections/index.dart';
import 'sections/watchtime.dart';
import 'sections/bottom_bar/watchtime.dart';
// ignore: must_be_immutable
class ShowPage extends StatefulWidget {
@ -90,12 +90,15 @@ class _ShowPageState extends State<ShowPage> with TickerProviderStateMixin {
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
height: _isBottomAppBarVisible ? 60 : 0.0,
child: const BottomAppBar(
shape: CircularNotchedRectangle(),
child: BottomAppBar(
shape: const CircularNotchedRectangle(),
clipBehavior: Clip.antiAlias,
notchMargin: 7.5,
color: kSecondaryColor,
child: ShowPageBottonBar()),
child: ShowPageBottonBar(
show: show,
isThereInLists: _isThereInLists,
)),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
@ -112,7 +115,7 @@ class _ShowPageState extends State<ShowPage> with TickerProviderStateMixin {
duration: const Duration(milliseconds: 225),
vsync: this),
builder: (context) {
return ShowPageAddWatchTime(
return ShowPageAddWatchDate(
show: show,
);
})

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
class ActiveableButton extends StatelessWidget {
final String text;
final String? activeText;
final IconData icon;
final IconData? activeIcon;
final VoidCallback onTap;
final bool isActive;
final Color backgroundColor;
final Color? activeColor;
final EdgeInsets margin;
const ActiveableButton(
{Key? key,
required this.isActive,
required this.text,
this.activeText,
required this.icon,
this.activeIcon,
required this.onTap,
this.activeColor,
this.margin = const EdgeInsets.symmetric(vertical: 7.5),
this.backgroundColor = const Color(0xff2a425f)})
: super(key: key);
@override
Widget build(BuildContext context) {
return AnimatedContainer(
height: 50,
margin: margin,
width: MediaQuery.of(context).size.width - 100,
decoration: BoxDecoration(
color: isActive
? activeColor?.withOpacity(0.75) ??
backgroundColor.withOpacity(0.75)
: backgroundColor.withOpacity(0.75),
borderRadius: BorderRadius.circular(15)),
duration: const Duration(milliseconds: 150),
child: TextButton(
onPressed: onTap,
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
),
child: Row(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25),
child: Icon(
isActive ? activeIcon ?? icon : icon,
color: Colors.white,
size: 20,
),
),
Text(
isActive ? activeText ?? text : text,
style: const TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.w800),
),
],
)),
);
}
}

50
lib/widgets/toast.dart Normal file
View File

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class ToastWidget extends StatelessWidget {
final String mainText;
final String buttonText;
final Color buttonColor;
final VoidCallback buttonOnTap;
const ToastWidget(
{Key? key,
required this.mainText,
required this.buttonText,
required this.buttonColor,
required this.buttonOnTap})
: super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 15),
margin: EdgeInsets.zero,
width: double.infinity,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10.0),
color: Colors.white.withOpacity(0.95),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
mainText,
style: GoogleFonts.ubuntu(
fontWeight: FontWeight.w600,
),
),
TextButton(
onPressed: buttonOnTap,
style:
TextButton.styleFrom(primary: buttonColor.withOpacity(0.5)),
child: Text(
buttonText,
style: GoogleFonts.ubuntu(
fontWeight: FontWeight.w600, color: buttonColor),
))
],
),
);
}
}

View File

@ -15,6 +15,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
ansicolor:
dependency: "direct dev"
description:
name: ansicolor
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
args:
dependency: transitive
description:
@ -258,6 +265,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
flutter_riverpod:
dependency: "direct main"
description:
name: flutter_riverpod
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
flutter_slidable:
dependency: "direct main"
description:
@ -289,6 +303,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
fluttertoast:
dependency: "direct main"
description:
name: fluttertoast
url: "https://pub.dartlang.org"
source: hosted
version: "8.0.9"
font_awesome_flutter:
dependency: "direct main"
description:
@ -604,6 +625,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
riverpod:
dependency: transitive
description:
name: riverpod
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
rxdart:
dependency: transitive
description:
@ -770,6 +798,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.10.0"
state_notifier:
dependency: transitive
description:
name: state_notifier
url: "https://pub.dartlang.org"
source: hosted
version: "0.7.2+1"
stream_channel:
dependency: transitive
description:
@ -912,4 +947,4 @@ packages:
version: "3.1.0"
sdks:
dart: ">=2.17.0-206.0.dev <3.0.0"
flutter: ">=2.10.0-0"
flutter: ">=3.0.0"

View File

@ -19,6 +19,7 @@ dependencies:
font_awesome_flutter: ^10.1.0
google_fonts: ^3.0.1
ms_undraw: ^3.0.1+1
fluttertoast: ^8.0.9
flutter_spinkit: ^5.1.0
flutter_rating_bar: ^4.0.0
google_nav_bar: ^5.0.5
@ -35,6 +36,9 @@ dependencies:
http: ^0.13.4
cached_network_image: ^3.2.0
speech_to_text: ^5.6.0
flutter_riverpod: ^1.0.4
dev_dependencies:
flutter_test:
@ -43,6 +47,7 @@ dev_dependencies:
test: ^1.17.12
build_runner: ^2.1.7
hive_generator: ^1.1.2
ansicolor: ^2.0.1
flutter:
uses-material-design: true

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginStart="50dp"
android:background="@drawable/corner"
android:layout_marginEnd="50dp">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#CC000000"
android:paddingStart="0dp"
android:paddingTop="0dp"
android:paddingEnd="0dp"
android:paddingBottom="0dp"
android:textStyle="bold"
android:textColor="#FFFFFF"
tools:text="Toast should be short." />
</FrameLayout>