add first screens

This commit is contained in:
kodjodevf 2023-04-03 12:52:30 +01:00
parent 9a3f9ab545
commit 07b2a656fc
21 changed files with 362 additions and 113 deletions

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "interactive"
}

View file

@ -44,7 +44,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.mangayomi" applicationId "com.kodjodevf.mangayomi"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion flutter.minSdkVersion minSdkVersion flutter.minSdkVersion

View file

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mangayomi"> package="com.kodjodevf.mangayomi">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.

View file

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mangayomi"> package="com.kodjodevf.mangayomi">
<application <application
android:label="mangayomi" android:label="mangayomi"
android:name="${applicationName}" android:name="${applicationName}"

View file

@ -1,4 +1,4 @@
package com.example.mangayomi package com.kodjodevf.mangayomi
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity

View file

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mangayomi"> package="com.kodjodevf.mangayomi">
<!-- The INTERNET permission is required for development. Specifically, <!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc. to allow setting breakpoints, to provide hot reload, etc.

View file

@ -296,7 +296,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.example.mangayomi; PRODUCT_BUNDLE_IDENTIFIER = com.kodjodevf.mangayomi;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@ -424,7 +424,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.example.mangayomi; PRODUCT_BUNDLE_IDENTIFIER = com.kodjodevf.mangayomi;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -446,7 +446,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.example.mangayomi; PRODUCT_BUNDLE_IDENTIFIER = com.kodjodevf.mangayomi;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;

View file

@ -1,115 +1,24 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mangayomi/router/router.dart';
void main() { void main() {
runApp(const MyApp()); runApp(const ProviderScope(child: MyApp()));
} }
class MyApp extends StatelessWidget { class MyApp extends ConsumerWidget {
const MyApp({super.key}); const MyApp({super.key});
// This widget is the root of your application. // This widget is the root of your application.
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context, WidgetRef ref) {
return MaterialApp( final router = ref.watch(routerProvider);
title: 'Flutter Demo', return MaterialApp.router(
theme: ThemeData( debugShowCheckedModeBanner: false,
// This is the theme of your application. routeInformationParser: router.routeInformationParser,
// routerDelegate: router.routerDelegate,
// Try running your application with "flutter run". You'll see the routeInformationProvider: router.routeInformationProvider,
// application has a blue toolbar. Then, without quitting the app, try title: 'MangaYomi',
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
); );
} }
} }

85
lib/router/router.dart Normal file
View file

@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:mangayomi/views/browse/browse_screen.dart';
import 'package:mangayomi/views/general/general_screen.dart';
import 'package:mangayomi/views/history/history_screen.dart';
import 'package:mangayomi/views/library/library_screen.dart';
import 'package:mangayomi/views/more/more_screen.dart';
import 'package:mangayomi/views/updates/updates_screen.dart';
final routerProvider = Provider<GoRouter>((ref) {
final router = AsyncRouterNotifier();
return GoRouter(
initialLocation: '/library',
debugLogDiagnostics: false,
refreshListenable: router,
routes: router._routes,
);
});
class AsyncRouterNotifier extends ChangeNotifier {
List<RouteBase> get _routes => [
ShellRoute(
builder: (context, state, child) => GeneralScreen(child: child),
routes: [
GoRoute(
name: "library",
path: '/library',
builder: (context, state) => const LibraryScreen(),
pageBuilder: (context, state) => CustomTransition(
key: state.pageKey,
child: const LibraryScreen(),
),
),
GoRoute(
name: "updates",
path: '/updates',
builder: (context, state) => const UpdatesScreen(),
pageBuilder: (context, state) => CustomTransition(
key: state.pageKey,
child: const UpdatesScreen(),
),
),
GoRoute(
name: "history",
path: '/history',
builder: (context, state) => const HistoryScreen(),
pageBuilder: (context, state) => CustomTransition(
key: state.pageKey,
child: const HistoryScreen(),
),
),
GoRoute(
name: "browse",
path: '/browse',
builder: (context, state) => const BrowseScreen(),
pageBuilder: (context, state) => CustomTransition(
key: state.pageKey,
child: const BrowseScreen(),
),
),
GoRoute(
name: "more",
path: '/more',
builder: (context, state) => const MoreScreen(),
pageBuilder: (context, state) => CustomTransition(
key: state.pageKey,
child: const MoreScreen(),
),
),
]),
];
}
class CustomTransition extends CustomTransitionPage {
CustomTransition({required LocalKey key, required Widget child})
: super(
key: key,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(opacity: animation, child: child);
},
child: child,
);
}

View file

@ -0,0 +1,9 @@
import 'package:flutter/material.dart';
mediaHeight(BuildContext context, double data) {
return MediaQuery.of(context).size.height * data;
}
mediaWidth(BuildContext context, double data) {
return MediaQuery.of(context).size.width * data;
}

View file

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class BrowseScreen extends StatelessWidget {
const BrowseScreen({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

View file

@ -0,0 +1,131 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:hidable/hidable.dart';
import 'package:mangayomi/views/general/scroll_controller_provider.dart';
import 'package:mangayomi/utils/media_query.dart';
class GeneralScreen extends ConsumerStatefulWidget {
const GeneralScreen({super.key, required this.child});
final Widget child;
@override
ConsumerState<GeneralScreen> createState() => _GeneralScreenState();
}
class _GeneralScreenState extends ConsumerState<GeneralScreen> {
@override
Widget build(BuildContext context) {
final scrollController = ref.watch(scrollControllerProvider);
final route = GoRouter.of(context);
int currentIndex = route.location == '/library'
? 0
: route.location == '/updates'
? 1
: route.location == '/history'
? 2
: route.location == '/browse'
? 3
: 4;
return Scaffold(
body: widget.child,
bottomNavigationBar: SizedBox(
width: mediaWidth(context, 1),
height: route.location != '/library' &&
route.location != '/updates' &&
route.location != '/history' &&
route.location != '/browse' &&
route.location != '/more'
? 0
: null,
child: Hidable(
controller: scrollController,
wOpacity: true,
child: NavigationBarTheme(
data: NavigationBarThemeData(
labelTextStyle: MaterialStateProperty.all(
const TextStyle(fontWeight: FontWeight.w500)),
indicatorShape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
height: 20,
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
),
child: ClipRRect(
child: NavigationBar(
animationDuration: const Duration(seconds: 1),
selectedIndex: currentIndex,
destinations: const [
NavigationDestination(
selectedIcon: Icon(
Icons.collections_bookmark,
color: Colors.white,
),
icon: Icon(
Icons.collections_bookmark_outlined,
),
label: 'Library'),
NavigationDestination(
selectedIcon: Icon(
Icons.new_releases,
color: Colors.white,
),
icon: Icon(
Icons.new_releases_outlined,
),
label: 'Updates'),
NavigationDestination(
selectedIcon: Icon(
Icons.history,
color: Colors.white,
),
icon: Icon(
Icons.history_outlined,
),
label: "History"),
NavigationDestination(
selectedIcon: Icon(
Icons.explore,
color: Colors.white,
),
icon: Icon(
Icons.explore_outlined,
),
label: "Browse"),
NavigationDestination(
selectedIcon: Icon(
Icons.more_horiz,
color: Colors.white,
),
icon: Icon(
Icons.more_horiz_outlined,
),
label: "More"),
],
onDestinationSelected: (int newIndex) {
if (mounted) {
setState(() {
currentIndex = newIndex;
});
}
if (newIndex == 0) {
route.go('/library');
} else if (newIndex == 1) {
route.go('/updates');
} else if (newIndex == 2) {
route.go('/history');
} else if (newIndex == 3) {
route.go('/browse');
} else if (newIndex == 4) {
route.go('/more');
}
},
),
),
),
),
),
);
}
}

View file

@ -0,0 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final scrollControllerProvider =
Provider<ScrollController>((ref) => ScrollController());

View file

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class HistoryScreen extends StatelessWidget {
const HistoryScreen({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

View file

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class LibraryScreen extends StatelessWidget {
const LibraryScreen({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

View file

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class MoreScreen extends StatelessWidget {
const MoreScreen({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

View file

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class UpdatesScreen extends StatelessWidget {
const UpdatesScreen({super.key});
@override
Widget build(BuildContext context) {
return const Placeholder();
}
}

View file

@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
set(BINARY_NAME "mangayomi") set(BINARY_NAME "mangayomi")
# The unique GTK application identifier for this application. See: # The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID # https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.mangayomi") set(APPLICATION_ID "com.kodjodevf.mangayomi")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake. # versions of CMake.

View file

@ -8,7 +8,7 @@
PRODUCT_NAME = mangayomi PRODUCT_NAME = mangayomi
// The application's bundle identifier // The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.example.mangayomi PRODUCT_BUNDLE_IDENTIFIER = com.kodjodevf.mangayomi
// The copyright displayed in application information // The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved.

View file

@ -70,11 +70,40 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
flutter_riverpod:
dependency: "direct main"
description:
name: flutter_riverpod
sha256: b3c3a8a9714b7f88dd2a41e1efbc47f76d620b06ab427c62ae7bc82298cd7dbb
url: "https://pub.dev"
source: hosted
version: "2.3.2"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
go_router:
dependency: "direct main"
description:
name: go_router
sha256: "6af43e0948b9f64a812afbb9ac3f76e2c8f5abe4efaba4188702a56af46b98c2"
url: "https://pub.dev"
source: hosted
version: "6.5.2"
hidable:
dependency: "direct main"
description:
name: hidable
sha256: abe6f3ce1037a8de18e996d1e6417306949217010daea56799d6203a9d8f1c48
url: "https://pub.dev"
source: hosted
version: "1.0.3"
js: js:
dependency: transitive dependency: transitive
description: description:
@ -91,6 +120,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
logging:
dependency: transitive
description:
name: logging
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -123,6 +160,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.2" version: "1.8.2"
riverpod:
dependency: transitive
description:
name: riverpod
sha256: b0fbf7927333c5c318f7e2c22c8b4fd2542ba294de0373e80ecdb34e0dcd8dc4
url: "https://pub.dev"
source: hosted
version: "2.3.2"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -144,6 +189,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.11.0" version: "1.11.0"
state_notifier:
dependency: transitive
description:
name: state_notifier
sha256: "8fe42610f179b843b12371e40db58c9444f8757f8b69d181c97e50787caed289"
url: "https://pub.dev"
source: hosted
version: "0.7.2+1"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
@ -186,3 +239,4 @@ packages:
version: "2.1.4" version: "2.1.4"
sdks: sdks:
dart: ">=2.19.5 <3.0.0" dart: ">=2.19.5 <3.0.0"
flutter: ">=3.3.0"

View file

@ -30,6 +30,9 @@ environment:
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
go_router: ^6.5.2
flutter_riverpod: ^2.3.2
hidable: ^1.0.3
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.