mirror of
https://github.com/simon-ding/polaris.git
synced 2026-02-06 23:21:00 +08:00
use riverpod
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:ui/navdrawer.dart';
|
||||
import 'package:ui/search.dart';
|
||||
@@ -46,7 +47,7 @@ class MyApp extends StatelessWidget {
|
||||
// Center is a layout widget. It takes a single child and positions it
|
||||
// in the middle of the parent.
|
||||
child: Row(children: <Widget>[
|
||||
NavDrawer(),
|
||||
const NavDrawer(),
|
||||
const VerticalDivider(thickness: 1, width: 1),
|
||||
Expanded(child: child)
|
||||
]))),
|
||||
@@ -81,29 +82,31 @@ class MyApp extends StatelessWidget {
|
||||
],
|
||||
);
|
||||
|
||||
return MaterialApp.router(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// TRY THIS: Try running your application with "flutter run". You'll see
|
||||
// the application has a purple toolbar. Then, without quitting the app,
|
||||
// try changing the seedColor in the colorScheme below to Colors.green
|
||||
// and then invoke "hot reload" (save your changes or press the "hot
|
||||
// reload" button in a Flutter-supported IDE, or press "r" if you used
|
||||
// the command line to start the app).
|
||||
//
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// state is not lost during the reload. To reset the state, use hot
|
||||
// restart instead.
|
||||
//
|
||||
// This works for code too, not just values: Most code changes can be
|
||||
// tested with just a hot reload.
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Colors.blue, brightness: Brightness.dark),
|
||||
useMaterial3: true,
|
||||
return ProviderScope(
|
||||
child: MaterialApp.router(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
// This is the theme of your application.
|
||||
//
|
||||
// TRY THIS: Try running your application with "flutter run". You'll see
|
||||
// the application has a purple toolbar. Then, without quitting the app,
|
||||
// try changing the seedColor in the colorScheme below to Colors.green
|
||||
// and then invoke "hot reload" (save your changes or press the "hot
|
||||
// reload" button in a Flutter-supported IDE, or press "r" if you used
|
||||
// the command line to start the app).
|
||||
//
|
||||
// Notice that the counter didn't reset back to zero; the application
|
||||
// state is not lost during the reload. To reset the state, use hot
|
||||
// restart instead.
|
||||
//
|
||||
// This works for code too, not just values: Most code changes can be
|
||||
// tested with just a hot reload.
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Colors.blue, brightness: Brightness.dark),
|
||||
useMaterial3: true,
|
||||
),
|
||||
routerConfig: _router,
|
||||
),
|
||||
routerConfig: _router,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
82
ui/lib/providers/series_details.dart
Normal file
82
ui/lib/providers/series_details.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
|
||||
var seriesDetailsProvider = FutureProvider.family((ref, seriesId) async {
|
||||
var resp = await Dio().get("${APIs.seriesDetailUrl}$seriesId");
|
||||
var rsp = ServerResponse.fromJson(resp.data);
|
||||
if (rsp.code != 0) {
|
||||
throw rsp.message;
|
||||
}
|
||||
return SeriesDetails.fromJson(rsp.data);
|
||||
});
|
||||
|
||||
class SeriesDetails {
|
||||
int? id;
|
||||
int? tmdbId;
|
||||
String? name;
|
||||
String? originalName;
|
||||
String? overview;
|
||||
String? path;
|
||||
String? posterPath;
|
||||
String? createdAt;
|
||||
List<Episodes>? episodes;
|
||||
|
||||
SeriesDetails(
|
||||
{this.id,
|
||||
this.tmdbId,
|
||||
this.name,
|
||||
this.originalName,
|
||||
this.overview,
|
||||
this.path,
|
||||
this.posterPath,
|
||||
this.createdAt,
|
||||
this.episodes});
|
||||
|
||||
SeriesDetails.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
tmdbId = json['tmdb_id'];
|
||||
name = json['name'];
|
||||
originalName = json['original_name'];
|
||||
overview = json['overview'];
|
||||
path = json['path'];
|
||||
posterPath = json['poster_path'];
|
||||
createdAt = json['created_at'];
|
||||
if (json['episodes'] != null) {
|
||||
episodes = <Episodes>[];
|
||||
json['episodes'].forEach((v) {
|
||||
episodes!.add(Episodes.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Episodes {
|
||||
int? id;
|
||||
int? seriesId;
|
||||
int? episodeNumber;
|
||||
String? title;
|
||||
String? airDate;
|
||||
int? seasonNumber;
|
||||
String? overview;
|
||||
|
||||
Episodes(
|
||||
{this.id,
|
||||
this.seriesId,
|
||||
this.episodeNumber,
|
||||
this.title,
|
||||
this.airDate,
|
||||
this.seasonNumber,
|
||||
this.overview});
|
||||
|
||||
Episodes.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
seriesId = json['series_id'];
|
||||
episodeNumber = json['episode_number'];
|
||||
title = json['title'];
|
||||
airDate = json['air_date'];
|
||||
seasonNumber = json['season_number'];
|
||||
overview = json['overview'];
|
||||
}
|
||||
}
|
||||
58
ui/lib/providers/welcome_data.dart
Normal file
58
ui/lib/providers/welcome_data.dart
Normal file
@@ -0,0 +1,58 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
|
||||
final welcomePageDataProvider = FutureProvider((ref) async {
|
||||
var resp = await Dio().get(APIs.watchlistUrl);
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
List<TvSeries> favList = List.empty(growable: true);
|
||||
for (var item in sp.data as List) {
|
||||
var tv = TvSeries.fromJson(item);
|
||||
favList.add(tv);
|
||||
}
|
||||
return favList;
|
||||
});
|
||||
|
||||
class TvSeries {
|
||||
int? id;
|
||||
int? tmdbId;
|
||||
String? name;
|
||||
String? originalName;
|
||||
String? overview;
|
||||
String? path;
|
||||
String? posterPath;
|
||||
|
||||
TvSeries(
|
||||
{this.id,
|
||||
this.tmdbId,
|
||||
this.name,
|
||||
this.originalName,
|
||||
this.overview,
|
||||
this.path,
|
||||
this.posterPath});
|
||||
|
||||
TvSeries.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
tmdbId = json['tmdb_id'];
|
||||
name = json['name'];
|
||||
originalName = json['original_name'];
|
||||
overview = json['overview'];
|
||||
path = json['path'];
|
||||
posterPath = json["poster_path"];
|
||||
}
|
||||
}
|
||||
|
||||
var tmdbApiSettingProvider = FutureProvider(
|
||||
(ref) async {
|
||||
final dio = Dio();
|
||||
var resp = await dio
|
||||
.get(APIs.settingsUrl, queryParameters: {"key": APIs.tmdbApiKey});
|
||||
var rrr = resp.data as Map<String, dynamic>;
|
||||
var data = rrr["data"] as Map<String, dynamic>;
|
||||
var key = data[APIs.tmdbApiKey] as String;
|
||||
|
||||
return key;
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/providers/welcome_data.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
import 'package:ui/utils.dart';
|
||||
|
||||
class SystemSettingsPage extends StatefulWidget {
|
||||
class SystemSettingsPage extends ConsumerStatefulWidget {
|
||||
static const route = "/systemsettings";
|
||||
|
||||
const SystemSettingsPage({super.key});
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
ConsumerState<ConsumerStatefulWidget> createState() {
|
||||
return _SystemSettingsPageState();
|
||||
}
|
||||
}
|
||||
|
||||
class _SystemSettingsPageState extends State<SystemSettingsPage> {
|
||||
class _SystemSettingsPageState extends ConsumerState<SystemSettingsPage> {
|
||||
final GlobalKey _formKey = GlobalKey<FormState>();
|
||||
final TextEditingController _tmdbApiKeyController = TextEditingController();
|
||||
|
||||
List<dynamic> indexers = List.empty();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_handleRefresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.fromLTRB(40, 10, 40, 0),
|
||||
child: RefreshIndicator(
|
||||
onRefresh: _handleRefresh,
|
||||
var key = ref.watch(tmdbApiSettingProvider);
|
||||
|
||||
return key.when(
|
||||
data: (data ) => Container(
|
||||
padding: const EdgeInsets.fromLTRB(40, 10, 40, 0),
|
||||
child: Form(
|
||||
key: _formKey, //设置globalKey,用于后面获取FormState
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
@@ -37,7 +40,7 @@ class _SystemSettingsPageState extends State<SystemSettingsPage> {
|
||||
children: [
|
||||
TextFormField(
|
||||
autofocus: true,
|
||||
controller: _tmdbApiKeyController,
|
||||
initialValue: data,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "TMDB Api Key",
|
||||
icon: Icon(Icons.key),
|
||||
@@ -46,6 +49,9 @@ class _SystemSettingsPageState extends State<SystemSettingsPage> {
|
||||
validator: (v) {
|
||||
return v!.trim().isNotEmpty ? null : "ApiKey 不能为空";
|
||||
},
|
||||
onSaved: (newValue) {
|
||||
_submitSettings(context, newValue!);
|
||||
},
|
||||
),
|
||||
Center(
|
||||
child: Padding(
|
||||
@@ -60,7 +66,7 @@ class _SystemSettingsPageState extends State<SystemSettingsPage> {
|
||||
// 调用validate()方法校验用户名密码是否合法,校验
|
||||
// 通过后再提交数据。
|
||||
if ((_formKey.currentState as FormState).validate()) {
|
||||
_submitSettings(context, _tmdbApiKeyController.text);
|
||||
(_formKey.currentState as FormState).save();
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -68,20 +74,10 @@ class _SystemSettingsPageState extends State<SystemSettingsPage> {
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleRefresh() async {
|
||||
final dio = Dio();
|
||||
var resp = await dio
|
||||
.get(APIs.settingsUrl, queryParameters: {"key": APIs.tmdbApiKey});
|
||||
var rrr = resp.data as Map<String, dynamic>;
|
||||
var data = rrr["data"] as Map<String, dynamic>;
|
||||
var key = data[APIs.tmdbApiKey] as String;
|
||||
_tmdbApiKeyController.text = key;
|
||||
|
||||
// Fetch new data and update the UI
|
||||
),
|
||||
),
|
||||
error: (err, trace) => Text("$err"),
|
||||
loading: () => const CircularProgressIndicator());
|
||||
}
|
||||
|
||||
void _submitSettings(BuildContext context, String v) async {
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/providers/series_details.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
import 'package:ui/utils.dart';
|
||||
|
||||
class TvDetailsPage extends StatefulWidget {
|
||||
class TvDetailsPage extends ConsumerStatefulWidget {
|
||||
static const route = "/series/:id";
|
||||
|
||||
static String toRoute(int id) {
|
||||
@@ -16,140 +18,125 @@ class TvDetailsPage extends StatefulWidget {
|
||||
const TvDetailsPage({super.key, required this.seriesId});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
ConsumerState<ConsumerStatefulWidget> createState() {
|
||||
return _TvDetailsPageState(seriesId: seriesId);
|
||||
}
|
||||
}
|
||||
|
||||
class _TvDetailsPageState extends State<TvDetailsPage> {
|
||||
class _TvDetailsPageState extends ConsumerState<TvDetailsPage> {
|
||||
final String seriesId;
|
||||
|
||||
_TvDetailsPageState({required this.seriesId});
|
||||
|
||||
SeriesDetails? details;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_querySeriesDetails();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (details == null) {
|
||||
return const Center(
|
||||
child: Text("nothing here"),
|
||||
);
|
||||
}
|
||||
|
||||
Map<int, List<Widget>> m = Map();
|
||||
for (final ep in details!.episodes!) {
|
||||
var w = Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 70,
|
||||
child: Text("第 ${ep.episodeNumber} 集"),
|
||||
),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Opacity(
|
||||
opacity: 0.5,
|
||||
child: Text("${ep.airDate}"),
|
||||
),
|
||||
),
|
||||
Text("${ep.title}", textAlign: TextAlign.left),
|
||||
const Expanded(child: Text("")),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_searchAndDownload(
|
||||
context, seriesId, ep.seasonNumber!, ep.episodeNumber!);
|
||||
},
|
||||
icon: const Icon(Icons.search))
|
||||
],
|
||||
),
|
||||
);
|
||||
if (m[ep.seasonNumber] == null) {
|
||||
m[ep.seasonNumber!] = List.empty(growable: true);
|
||||
}
|
||||
m[ep.seasonNumber!]!.add(w);
|
||||
}
|
||||
List<ExpansionTile> list = List.empty(growable: true);
|
||||
for (final k in m.keys.toList().reversed) {
|
||||
bool _customTileExpanded = false;
|
||||
var seasonList = ExpansionTile(
|
||||
tilePadding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(50, 0, 50, 0),
|
||||
initiallyExpanded: k == 0 ? false : true,
|
||||
title: Text("第 $k 季"),
|
||||
trailing: Icon(
|
||||
_customTileExpanded
|
||||
? Icons.arrow_drop_down_circle
|
||||
: Icons.arrow_drop_down,
|
||||
),
|
||||
children: m[k]!,
|
||||
onExpansionChanged: (bool expanded) {
|
||||
setState(() {
|
||||
_customTileExpanded = expanded;
|
||||
});
|
||||
},
|
||||
);
|
||||
list.add(seasonList);
|
||||
}
|
||||
|
||||
return ListView(
|
||||
children: [
|
||||
Card(
|
||||
margin: const EdgeInsets.all(4),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: SizedBox(
|
||||
width: 150,
|
||||
height: 200,
|
||||
child: Image.network(
|
||||
APIs.tmdbImgBaseUrl + details!.posterPath!,
|
||||
fit: BoxFit.contain,
|
||||
var seriesDetails = ref.watch(seriesDetailsProvider(seriesId));
|
||||
return seriesDetails.when(
|
||||
data: (details) {
|
||||
Map<int, List<Widget>> m = Map();
|
||||
for (final ep in details.episodes!) {
|
||||
var w = Container(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 70,
|
||||
child: Text("第 ${ep.episodeNumber} 集"),
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${details!.name}",
|
||||
style: const TextStyle(
|
||||
fontSize: 14, fontWeight: FontWeight.bold),
|
||||
SizedBox(
|
||||
width: 100,
|
||||
child: Opacity(
|
||||
opacity: 0.5,
|
||||
child: Text("${ep.airDate}"),
|
||||
),
|
||||
),
|
||||
Text("${ep.title}", textAlign: TextAlign.left),
|
||||
const Expanded(child: Text("")),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_searchAndDownload(context, seriesId, ep.seasonNumber!,
|
||||
ep.episodeNumber!);
|
||||
},
|
||||
icon: const Icon(Icons.search))
|
||||
],
|
||||
),
|
||||
);
|
||||
if (m[ep.seasonNumber] == null) {
|
||||
m[ep.seasonNumber!] = List.empty(growable: true);
|
||||
}
|
||||
m[ep.seasonNumber!]!.add(w);
|
||||
}
|
||||
List<ExpansionTile> list = List.empty(growable: true);
|
||||
for (final k in m.keys.toList().reversed) {
|
||||
bool _customTileExpanded = false;
|
||||
var seasonList = ExpansionTile(
|
||||
tilePadding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
|
||||
childrenPadding: const EdgeInsets.fromLTRB(50, 0, 50, 0),
|
||||
initiallyExpanded: k == 0 ? false : true,
|
||||
title: Text("第 $k 季"),
|
||||
trailing: Icon(
|
||||
_customTileExpanded
|
||||
? Icons.arrow_drop_down_circle
|
||||
: Icons.arrow_drop_down,
|
||||
),
|
||||
children: m[k]!,
|
||||
onExpansionChanged: (bool expanded) {
|
||||
setState(() {
|
||||
_customTileExpanded = expanded;
|
||||
});
|
||||
},
|
||||
);
|
||||
list.add(seasonList);
|
||||
}
|
||||
return ListView(
|
||||
children: [
|
||||
Card(
|
||||
margin: const EdgeInsets.all(4),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: SizedBox(
|
||||
width: 150,
|
||||
height: 200,
|
||||
child: Image.network(
|
||||
APIs.tmdbImgBaseUrl + details!.posterPath!,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${details!.name}",
|
||||
style: const TextStyle(
|
||||
fontSize: 14, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Text(""),
|
||||
Text(details!.overview!)
|
||||
],
|
||||
),
|
||||
),
|
||||
const Text(""),
|
||||
Text(details!.overview!)
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: list,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: list,
|
||||
),
|
||||
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _querySeriesDetails() async {
|
||||
if (details != null) {
|
||||
return;
|
||||
}
|
||||
var resp = await Dio().get("${APIs.seriesDetailUrl}$seriesId");
|
||||
var rsp = ServerResponse.fromJson(resp.data);
|
||||
|
||||
setState(() {
|
||||
details = SeriesDetails.fromJson(rsp.data);
|
||||
});
|
||||
);
|
||||
},
|
||||
error: (err, trace) {
|
||||
return Text("$err");
|
||||
},
|
||||
loading: () => const CircularProgressIndicator());
|
||||
}
|
||||
|
||||
void _searchAndDownload(BuildContext context, String seriesId, int seasonNum,
|
||||
@@ -170,72 +157,3 @@ class _TvDetailsPageState extends State<TvDetailsPage> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SeriesDetails {
|
||||
int? id;
|
||||
int? tmdbId;
|
||||
String? name;
|
||||
String? originalName;
|
||||
String? overview;
|
||||
String? path;
|
||||
String? posterPath;
|
||||
String? createdAt;
|
||||
List<Episodes>? episodes;
|
||||
|
||||
SeriesDetails(
|
||||
{this.id,
|
||||
this.tmdbId,
|
||||
this.name,
|
||||
this.originalName,
|
||||
this.overview,
|
||||
this.path,
|
||||
this.posterPath,
|
||||
this.createdAt,
|
||||
this.episodes});
|
||||
|
||||
SeriesDetails.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
tmdbId = json['tmdb_id'];
|
||||
name = json['name'];
|
||||
originalName = json['original_name'];
|
||||
overview = json['overview'];
|
||||
path = json['path'];
|
||||
posterPath = json['poster_path'];
|
||||
createdAt = json['created_at'];
|
||||
if (json['episodes'] != null) {
|
||||
episodes = <Episodes>[];
|
||||
json['episodes'].forEach((v) {
|
||||
episodes!.add(Episodes.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Episodes {
|
||||
int? id;
|
||||
int? seriesId;
|
||||
int? episodeNumber;
|
||||
String? title;
|
||||
String? airDate;
|
||||
int? seasonNumber;
|
||||
String? overview;
|
||||
|
||||
Episodes(
|
||||
{this.id,
|
||||
this.seriesId,
|
||||
this.episodeNumber,
|
||||
this.title,
|
||||
this.airDate,
|
||||
this.seasonNumber,
|
||||
this.overview});
|
||||
|
||||
Episodes.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
seriesId = json['series_id'];
|
||||
episodeNumber = json['episode_number'];
|
||||
title = json['title'];
|
||||
airDate = json['air_date'];
|
||||
seasonNumber = json['season_number'];
|
||||
overview = json['overview'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,26 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:ui/APIs.dart';
|
||||
import 'package:ui/server_response.dart';
|
||||
import 'package:ui/providers/welcome_data.dart';
|
||||
import 'package:ui/tv_details.dart';
|
||||
|
||||
class WelcomePage extends StatefulWidget {
|
||||
const WelcomePage({super.key});
|
||||
class WelcomePage extends ConsumerWidget {
|
||||
static const route = "/welcome";
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _WeclomePageState();
|
||||
}
|
||||
}
|
||||
|
||||
class _WeclomePageState extends State<WelcomePage> {
|
||||
var favList = List.empty(growable: true);
|
||||
const WelcomePage({super.key});
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_onRefresh();
|
||||
}
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final data = ref.watch(welcomePageDataProvider);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GridView.builder(
|
||||
itemCount: favList.length,
|
||||
return switch (data) {
|
||||
AsyncData(:final value) => GridView.builder(
|
||||
itemCount: value.length,
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 6),
|
||||
itemBuilder: (context, i) {
|
||||
var item = TvSeries.fromJson(favList[i]);
|
||||
var item = value[i];
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(4),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
@@ -63,46 +52,9 @@ class _WeclomePageState extends State<WelcomePage> {
|
||||
],
|
||||
),
|
||||
));
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onRefresh() async {
|
||||
if (favList.isNotEmpty) {
|
||||
return;
|
||||
}
|
||||
var resp = await Dio().get(APIs.watchlistUrl);
|
||||
var sp = ServerResponse.fromJson(resp.data);
|
||||
setState(() {
|
||||
favList = sp.data as List;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class TvSeries {
|
||||
int? id;
|
||||
int? tmdbId;
|
||||
String? name;
|
||||
String? originalName;
|
||||
String? overview;
|
||||
String? path;
|
||||
String? posterPath;
|
||||
|
||||
TvSeries(
|
||||
{this.id,
|
||||
this.tmdbId,
|
||||
this.name,
|
||||
this.originalName,
|
||||
this.overview,
|
||||
this.path,
|
||||
this.posterPath});
|
||||
|
||||
TvSeries.fromJson(Map<String, dynamic> json) {
|
||||
id = json['id'];
|
||||
tmdbId = json['tmdb_id'];
|
||||
name = json['name'];
|
||||
originalName = json['original_name'];
|
||||
overview = json['overview'];
|
||||
path = json['path'];
|
||||
posterPath = json["poster_path"];
|
||||
}),
|
||||
_ => const CircularProgressIndicator(),
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
flutter_riverpod:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_riverpod
|
||||
sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.5.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@@ -176,6 +184,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: riverpod
|
||||
sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.5.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@@ -197,6 +213,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
state_notifier:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: state_notifier
|
||||
sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -37,6 +37,7 @@ dependencies:
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.6
|
||||
go_router: ^14.2.0
|
||||
flutter_riverpod: ^2.5.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user