chore: bootstrap flutter reader app skeleton
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../shared/widgets/feature_placeholder.dart';
|
||||
|
||||
class LoginScreen extends StatelessWidget {
|
||||
const LoginScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Dang nhap')),
|
||||
body: const FeaturePlaceholder(
|
||||
title: 'Google Login',
|
||||
description:
|
||||
'Khung dang nhap Google OAuth cho mobile auth endpoint. Se bo sung token refresh va secure storage trong phase tiep theo.',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../shared/widgets/feature_placeholder.dart';
|
||||
|
||||
class BookshelfScreen extends StatelessWidget {
|
||||
const BookshelfScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: 4,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Tu sach'),
|
||||
bottom: const TabBar(
|
||||
isScrollable: true,
|
||||
tabs: [
|
||||
Tab(text: 'Dang doc'),
|
||||
Tab(text: 'Danh dau'),
|
||||
Tab(text: 'Da doc'),
|
||||
Tab(text: 'De cu'),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: const TabBarView(
|
||||
children: [
|
||||
FeaturePlaceholder(
|
||||
title: 'Dang doc',
|
||||
description: 'Danh sach truyện dang doc theo progress sync.',
|
||||
),
|
||||
FeaturePlaceholder(
|
||||
title: 'Danh dau',
|
||||
description: 'Tat ca truyện da bookmark cua user.',
|
||||
),
|
||||
FeaturePlaceholder(
|
||||
title: 'Da doc',
|
||||
description: 'Danh sach truyện da hoan thanh.',
|
||||
),
|
||||
FeaturePlaceholder(
|
||||
title: 'De cu',
|
||||
description: 'Danh sach truyện user da de cu.',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CommentsScreen extends StatelessWidget {
|
||||
const CommentsScreen({
|
||||
super.key,
|
||||
required this.novelId,
|
||||
this.chapterId,
|
||||
});
|
||||
|
||||
final String novelId;
|
||||
final String? chapterId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Binh luan')),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
Text('Novel ID: ${novelId.isEmpty ? '(missing)' : novelId}'),
|
||||
Text('Chapter ID: ${chapterId ?? '(all novel comments)'}'),
|
||||
const SizedBox(height: 12),
|
||||
const Text('Khung danh sach binh luan + form gui comment + phan trang.'),
|
||||
const SizedBox(height: 20),
|
||||
TextField(
|
||||
maxLines: 4,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Viet binh luan cua ban...',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
FilledButton(
|
||||
onPressed: () {},
|
||||
child: const Text('Gui binh luan'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../shared/widgets/feature_placeholder.dart';
|
||||
|
||||
class GenresScreen extends StatelessWidget {
|
||||
const GenresScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('The loai')),
|
||||
body: const FeaturePlaceholder(
|
||||
title: 'Genre Discovery',
|
||||
description:
|
||||
'Khung danh sach the loai va man hinh truyện theo the loai slug de dong bo hanh vi voi web.',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../app/router/route_names.dart';
|
||||
import '../../../shared/widgets/feature_placeholder.dart';
|
||||
|
||||
class HomeScreen extends StatelessWidget {
|
||||
const HomeScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Trang chu')),
|
||||
body: FeaturePlaceholder(
|
||||
title: 'Home Feed',
|
||||
description:
|
||||
'Khung trang chu cho carousel hot, random grid, bang de cu, bang xep hang, truyện moi cap nhat va comments gan day.',
|
||||
actions: [
|
||||
FilledButton(
|
||||
onPressed: () => context.go(RouteNames.search),
|
||||
child: const Text('Mo tim kiem'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../app/router/route_names.dart';
|
||||
|
||||
class NovelDetailScreen extends StatelessWidget {
|
||||
const NovelDetailScreen({super.key, required this.novelId});
|
||||
|
||||
final String novelId;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Chi tiet truyện')),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
Text('Novel ID: ${novelId.isEmpty ? '(missing)' : novelId}'),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
'Khung chi tiet truyện: metadata, series, chapter list, rating, bookmark, recommendation, comments.',
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: [
|
||||
FilledButton(
|
||||
onPressed: () => context.push('${RouteNames.reader}?chapterId=1'),
|
||||
child: const Text('Doc chuong 1'),
|
||||
),
|
||||
OutlinedButton(
|
||||
onPressed: () =>
|
||||
context.push('${RouteNames.comments}?novelId=$novelId'),
|
||||
child: const Text('Xem binh luan'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../app/router/route_names.dart';
|
||||
import '../../../shared/widgets/feature_placeholder.dart';
|
||||
|
||||
class ProfileScreen extends StatelessWidget {
|
||||
const ProfileScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Tai khoan')),
|
||||
body: FeaturePlaceholder(
|
||||
title: 'User Profile',
|
||||
description:
|
||||
'Khung profile user, thong tin session, thong ke bookmark/de cu va cac cai dat doc dong bo.',
|
||||
actions: [
|
||||
FilledButton(
|
||||
onPressed: () => context.push(RouteNames.settings),
|
||||
child: const Text('Mo cai dat doc'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../app/router/route_names.dart';
|
||||
|
||||
class ReaderScreen extends StatefulWidget {
|
||||
const ReaderScreen({super.key, required this.chapterId});
|
||||
|
||||
final String chapterId;
|
||||
|
||||
@override
|
||||
State<ReaderScreen> createState() => _ReaderScreenState();
|
||||
}
|
||||
|
||||
class _ReaderScreenState extends State<ReaderScreen> {
|
||||
double fontSize = 18;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Doc chuong ${widget.chapterId.isEmpty ? '?' : widget.chapterId}'),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => context.push(RouteNames.settings),
|
||||
icon: const Icon(Icons.tune),
|
||||
tooltip: 'Cai dat doc',
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text('Reader body placeholder with TOC, TTS, offline marker.'),
|
||||
const SizedBox(height: 12),
|
||||
Text('Co chu hien tai: ${fontSize.toStringAsFixed(0)}'),
|
||||
Slider(
|
||||
min: 14,
|
||||
max: 26,
|
||||
value: fontSize,
|
||||
onChanged: (v) => setState(() => fontSize = v),
|
||||
),
|
||||
const Spacer(),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
onPressed: () {},
|
||||
child: const Text('Chuong truoc'),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: FilledButton(
|
||||
onPressed: () {},
|
||||
child: const Text('Chuong sau'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.small(
|
||||
onPressed: () {},
|
||||
child: const Icon(Icons.record_voice_over),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../shared/widgets/feature_placeholder.dart';
|
||||
|
||||
class SearchScreen extends StatelessWidget {
|
||||
const SearchScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Tim kiem')),
|
||||
body: const FeaturePlaceholder(
|
||||
title: 'Search + Filters',
|
||||
description:
|
||||
'Khung tim kiem truyện voi goi y theo tu khoa, loc theo the loai/trang thai va sap xep theo views-rating-latest.',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
const SettingsScreen({super.key});
|
||||
|
||||
@override
|
||||
State<SettingsScreen> createState() => _SettingsScreenState();
|
||||
}
|
||||
|
||||
class _SettingsScreenState extends State<SettingsScreen> {
|
||||
double fontSize = 18;
|
||||
double lineHeight = 1.8;
|
||||
double letterSpacing = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Cai dat doc')),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
Text('Co chu: ${fontSize.toStringAsFixed(0)}'),
|
||||
Slider(
|
||||
min: 14,
|
||||
max: 26,
|
||||
value: fontSize,
|
||||
onChanged: (v) => setState(() => fontSize = v),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text('Line-height: ${lineHeight.toStringAsFixed(1)}'),
|
||||
Slider(
|
||||
min: 1.2,
|
||||
max: 2.4,
|
||||
value: lineHeight,
|
||||
onChanged: (v) => setState(() => lineHeight = v),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text('Letter-spacing: ${letterSpacing.toStringAsFixed(1)}'),
|
||||
Slider(
|
||||
min: -0.5,
|
||||
max: 2,
|
||||
value: letterSpacing,
|
||||
onChanged: (v) => setState(() => letterSpacing = v),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
FilledButton(
|
||||
onPressed: () {},
|
||||
child: const Text('Luu va dong bo'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../app/router/route_names.dart';
|
||||
|
||||
class SplashScreen extends StatefulWidget {
|
||||
const SplashScreen({super.key});
|
||||
|
||||
@override
|
||||
State<SplashScreen> createState() => _SplashScreenState();
|
||||
}
|
||||
|
||||
class _SplashScreenState extends State<SplashScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future<void>.delayed(const Duration(milliseconds: 700), () {
|
||||
if (!mounted) return;
|
||||
context.go(RouteNames.home);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.menu_book_rounded, size: 48),
|
||||
SizedBox(height: 12),
|
||||
Text('Reader App'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user