提交 a5ca16e2 authored 作者: yueweilu's avatar yueweilu

书架界面

上级 d9bb1748
......@@ -25,7 +25,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(428, 926),
designSize: const Size(375, 667),
builder: (context, child) => RefreshConfiguration(
headerBuilder: () => const ClassicHeader(
refreshingIcon: CupertinoActivityIndicator(),
......
library about;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_book/widgets/index.dart';
part 'view.dart';
\ No newline at end of file
part of about;
class AboutPage extends StatelessWidget {
const AboutPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SafeArea(
top: false,
child: Scaffold(
appBar: AppBar(
title: Text('关于我们'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
// crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
children: [
Container(
margin: EdgeInsets.only(top: 40),
height: 50,
width: 50,
color: Colors.cyan,
child: CustomImage.asset(url: 'assets/images/banner.png'),
),
Text('云教材'),
Text('V1.0.1'),
],
),
Container(
color: Colors.cyan,
alignment: Alignment.center,
height: 50,
child: Row(
mainAxisAlignment:MainAxisAlignment.spaceBetween,
children: [
Expanded(child: Text('《用户协议》',textAlign: TextAlign.right,)),
Expanded(child: Text('《隐私协议》',textAlign: TextAlign.left)),
],
),
)
],
),
),
);
}
}
part of book_shop;
\ No newline at end of file
library book_shop;
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../theme.dart';
part 'controller.dart';
part 'view.dart';
part 'widgets/book.dart';
part 'widgets/count.dart';
\ No newline at end of file
part of book_shop;
class BookShopPage extends StatefulWidget {
const BookShopPage({Key? key}) : super(key: key);
@override
State<BookShopPage> createState() => _BookShopPageState();
}
class _BookShopPageState extends State<BookShopPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('书架'),
),
body: Column(
children: [
Expanded(
child: ListView.builder(
itemBuilder: (BuildContext context,int index){
return const BookCell();
},
itemCount: 2,
),
),
createCounter()
],
),
);
}
Widget createCounter() {
return Container(
color: Colors.white,
padding: EdgeInsets.only(left: 20,right: 15),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
child: Row(
children: [
Container(
height: 17,
width: 17,
color: Colors.yellow,
),
SizedBox(width: 11),
Text('全选',style: TextStyle(color: Color(0xFF333333),fontSize: 12,fontWeight: FontWeight.w500)),
SizedBox(width: 15,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('合计 ¥98.9',style: TextStyle(color: AppTheme.primary,fontSize: 12,fontWeight: FontWeight.w500)),
Text('已选 2 件',style: TextStyle(color: Color(0xFF9999999),fontSize: 10,))
],
)
],
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 9),
decoration: BoxDecoration(
color: AppTheme.primary,
borderRadius: BorderRadius.circular(15),
),
padding: EdgeInsets.symmetric(vertical: 5,horizontal: 18),
child: Text('结算(2)',style: TextStyle(color: Colors.white,fontSize: 12,fontWeight: FontWeight.w500),),
)
],
),
Container(
height: 1,
color: Color(0xFFF9F9F9),
)
],
),
);
}
}
part of book_shop;
class BookCell extends StatelessWidget {
const BookCell({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius:BorderRadius.circular(5),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Color(0xFFC7C7C7).withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(3, 0), // changes the position of the shadow
),
]
),
margin: const EdgeInsets.only(left: 10,right: 10,top: 10),
height: 110,
child: Row(
children: [
///左侧
Container(
margin: const EdgeInsets.only(left: 12,right: 11),
child: Row(
children: [
Container(
height: 17,
width: 17,
color: Colors.cyan,
),
const SizedBox(width: 12,),
Container(
child: Container(
padding: EdgeInsets.all(2),
child: Container(
color: Colors.cyan,
),
),
decoration: BoxDecoration(
color: Colors.cyan,
borderRadius: BorderRadius.circular(3),
boxShadow: [
BoxShadow(
color: Color(0xFF707070).withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
offset: Offset(3, 0), // changes the position of the shadow
),
]
),
// color: Colors.white,
height: 86,
width: 72,
)
],
),
),
///右侧
Expanded(
child: Container(
padding: const EdgeInsets.only(top: 12,bottom: 10),
child: const Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('一想到还有95%的问题留给人类,我就放一想到还有95%的问题留给人类,我就放一想到还有95%的问题留给人类,我就放',style: TextStyle(fontSize: 13,fontWeight: FontWeight.w500,color: Color(0xFF333333)),maxLines: 2,overflow: TextOverflow.ellipsis,),
SizedBox(height: 5,),
Text('威廉·莎士比亚',style: TextStyle(fontSize: 11,fontWeight: FontWeight.w400,color: Color(0xFF999999))),
],
),
Text('¥88',style: TextStyle(fontSize: 14,fontWeight: FontWeight.w500,color: AppTheme.primary)),
],
),
),
)
],
)
);
}
}
part of book_shop;
class Counter extends StatelessWidget {
const Counter({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [
GestureDetector(
onTap: (){
},
child: Row(
children: [
Container(width: 20,height: 20,color: Colors.cyan,),
const SizedBox(width: 11,),
const Text('全选',style: TextStyle(color: Color(0xFF333333),fontSize: 12,fontWeight:FontWeight.w500)),
],
),
),
Column(
children: [
Text('合计',style: TextStyle(color: AppTheme.primary)),
Text('已选1件',style: TextStyle(color: Color(0xFF333333)),)
],
),
],
);
}
}
......@@ -17,13 +17,31 @@ class _HomePageState extends State<HomePage> {
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ElevatedButton(
onPressed: (){
GestureDetector(
onTap: (){
context.pushNamed(Routes.web);
},
child: const Text('web'),
child: Container(
color: Colors.cyan,
child: Text('web'),
height: 40,
// width: double.infinity,
alignment: Alignment.center,
),
),
GestureDetector(
onTap: (){
context.pushNamed(Routes.about);
},
child: Container(
color: Colors.indigo,
child: Text('关于我们'),
height: 40,
width: double.infinity,
alignment: Alignment.center,
),
)
],
)
);
......
......@@ -7,6 +7,7 @@ import 'package:flutter_book/theme.dart';
import 'package:get/get.dart';
import 'package:ionicons/ionicons.dart';
import '../book_shop/index.dart';
import '../record/index.dart';
import '../tts/index.dart';
import '../web/index.dart';
......
......@@ -41,8 +41,8 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver{
children: const [
HomePage(),
LibraryPage(),
TestTTSPage(),
RecordPage(),
BookShopPage(),
// RecordPage(),
HomePage(),
],
),
......@@ -57,7 +57,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver{
BottomNavigationBarItem(
icon: Icon(Ionicons.home_outline),
activeIcon:Icon(Ionicons.home),
label: '发现',
label: '课程',
),
BottomNavigationBarItem(
icon: Icon(Ionicons.bookmarks),
......@@ -69,11 +69,6 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver{
activeIcon:Icon(Ionicons.chatbubbles),
label: '书架',
),
BottomNavigationBarItem(
icon: Icon(Ionicons.person_outline),
activeIcon:Icon(Ionicons.person),
label: '课程',
),
BottomNavigationBarItem(
icon: Icon(Ionicons.person_outline),
activeIcon:Icon(Ionicons.person),
......
part of mine;
\ No newline at end of file
library mine;
\ No newline at end of file
part of mine;
\ No newline at end of file
......@@ -4,16 +4,19 @@ class ReadController extends GetxController with GetSingleTickerProviderStateMix
late AnimationController _controller;
bool _show = false;
bool get show => _show;
void setShow(bool value) {
_show = !value;
if (_show) {
/// 显示状态栏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
/// 开启动画
_controller.forward();
}
else {
/// 不显示状态栏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
/// 收回动画
_controller.reverse();
}
update();
......@@ -25,7 +28,9 @@ class ReadController extends GetxController with GetSingleTickerProviderStateMix
@override
void onInit() {
/// 默认不显示状态栏
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
/// 初始化
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 100),
......
......@@ -8,8 +8,9 @@ class WebPage extends StatefulWidget {
}
class _WebPageState extends State<WebPage> {
final GlobalKey webViewKey = GlobalKey();
InAppWebViewController? webViewController;
late ContextMenu contextMenu;
@override
void initState() {
super.initState();
......@@ -19,42 +20,73 @@ class _WebPageState extends State<WebPage> {
return GetBuilder<ReadController>(
init: ReadController(),
builder: (readController) => Scaffold(
// appBar: AppBar(title:const Text('章节名称'),),
body: Stack(
children: [
Container(
height: 40,
width: double.infinity,
color: Colors.lightBlue,
),
InAppWebView(
initialUrlRequest: URLRequest(
url: Uri.parse('http://192.168.11.39:5500/'),
),
onWebViewCreated: (InAppWebViewController controller) {
webViewController = controller;
},
// onLoadError: (InAppWebViewController controller, Uri url, int code ,String msg){
//
// },
contextMenu: ContextMenu(
menuItems: [
ContextMenuItem(title: '划线',androidId: 1,iosId: '1'),
ContextMenuItem(title: '提问',androidId: 2,iosId: '2'),
ContextMenuItem(title: '笔记',androidId: 3,iosId: '3'),
ContextMenuItem(title: '划线1',androidId: 1,iosId: '1'),
ContextMenuItem(title: '划线2',androidId: 1,iosId: '1'),
ContextMenuItem(title: '划线3',androidId: 1,iosId: '1'),
ContextMenuItem(title: '划线4',androidId: 1,iosId: '1'),
ContextMenuItem(title: '划线',androidId: 1,iosId: '1',action: (){
const snackBar = SnackBar(
content: Text("划线"),
duration: Duration(seconds: 1),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}),
ContextMenuItem(title: '提问',androidId: 2,iosId: '2',action: (){
}),
ContextMenuItem(title: '笔记',androidId: 3,iosId: '3',action: (){
}),
],
options: ContextMenuOptions(
hideDefaultSystemContextMenuItems: false,
),
onContextMenuActionItemClicked: (ContextMenuItem item) async {
switch (item.title){
case '划线':
Console.log('----------划线-----------');
break;
case '提问':
Console.log('----------提问-----------');
break;
case '笔记':
Console.log('----------笔记-----------');
break;
onCreateContextMenu: (hitTestResult) async {
String selectedText = await webViewController?.getSelectedText() ?? "";
final snackBar = SnackBar(
content: Text(
"Selected text: '$selectedText', of type: ${hitTestResult.type.toString()}"),
duration: const Duration(seconds: 1),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
},
// options: ContextMenuOptions(
// hideDefaultSystemContextMenuItems: true,
// ),
onContextMenuActionItemClicked: (ContextMenuItem menuItem) async {
final snackBar = SnackBar(
content: Text(
"Menu item with ID ${menuItem.iosId} and title '${menuItem.title}' clicked!"),
duration: const Duration(seconds: 1),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
// switch (item.title){
// case '划线':
// Console.log('----------划线-----------');
// break;
// case '提问':
// Console.log('----------提问-----------');
// break;
// case '笔记':
// Console.log('----------笔记-----------');
// break;
//
// }
}
),
......
......@@ -2,6 +2,7 @@ library routes;
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_book/pages/about/index.dart';
import 'package:flutter_book/pages/ad/index.dart';
import 'package:flutter_book/pages/login/index.dart';
import 'package:flutter_book/pages/main/index.dart';
......
......@@ -9,6 +9,8 @@ abstract class Routes {
static const ad = 'ad';
static const login = 'login';
static const web = 'web';
static const about = 'about';
static final GoRouter config = GoRouter(
// initialLocation: '/$splash',
......@@ -59,6 +61,15 @@ abstract class Routes {
key: state.pageKey,
child: const WebPage()
)
),
GoRoute(
path: '/$about',
name: about,
pageBuilder: (context, state) =>CupertinoPage(
name: state.uri.toString(),
key: state.pageKey,
child: const AboutPage()
)
)
]
);
......
......@@ -7,10 +7,10 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
abstract class AppTheme {
// 应用程序默认边距
static const margin = 16.0;
static const margin = 10.0;
// 主要/主题颜色的常量
static const primary = Color(0xFF2972FE);
static const primary = Color(0xFFAB1941);
// 成功状态的颜色
static const success = Color(0xFF23A757);
// 警告状态的颜色
......@@ -71,7 +71,7 @@ abstract class AppTheme {
static ThemeData get light {
var scheme = ColorScheme.light(
background: Colors.white,
background: const Color(0xFFF9F9F9),
onBackground: const Color(0xFF333333),
surface: Colors.white,
onSurface: const Color(0xFF333333),
......@@ -81,7 +81,7 @@ abstract class AppTheme {
onSecondary: Colors.white,
tertiary: const Color(0xFFF4F6F9),
outline: const Color(0xFFF4F6F9),
shadow: const Color(0xFF5A6CEA).withOpacity(0.08),
shadow: const Color(0xFFAB1941).withOpacity(0.08),
error: error,
onError: Colors.white,
);
......@@ -140,8 +140,8 @@ abstract class AppTheme {
),
),
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle.light,
backgroundColor: scheme.background,
systemOverlayStyle: SystemUiOverlayStyle.dark,
backgroundColor: const Color(0x05AB1941),
scrolledUnderElevation: 0,
elevation: 0,
centerTitle: true,
......@@ -150,9 +150,10 @@ abstract class AppTheme {
color: scheme.onBackground,
size: 22.w,
),
titleTextStyle: TextStyle(
color: scheme.onBackground,
fontSize: 24.w,
fontSize: 17.w,
fontWeight: FontWeight.w600,
height: 1.2,
),
......@@ -185,14 +186,14 @@ abstract class AppTheme {
backgroundColor: scheme.background,
unselectedItemColor: scheme.onBackground.withOpacity(0.5),
selectedItemColor: scheme.primary,
unselectedLabelStyle: TextStyle(fontSize: 13.w, height: 1.6),
selectedLabelStyle: TextStyle(fontSize: 13.w, height: 1.6),
unselectedLabelStyle: TextStyle(fontSize: 10.w, height: 1.6),
selectedLabelStyle: TextStyle(fontSize: 10.w, height: 1.6),
unselectedIconTheme: IconThemeData(
size: 24.w,
size: 15.w,
color: scheme.onBackground.withOpacity(0.5),
),
selectedIconTheme: IconThemeData(
size: 24.w,
size: 15.w,
color: scheme.primary,
),
),
......
......@@ -17,4 +17,5 @@ part 'console.dart';
part 'app_config.dart';
part 'sign.dart';
part 'encrypt_util.dart';
part 'tools.dart';
\ No newline at end of file
part 'tools.dart';
part 'styles.dart';
\ No newline at end of file
part of utils;
class TextStyles {
static const TextStyle c3F13M = TextStyle(
fontSize: 13,
color: Color(0xFF333333),
fontWeight: FontWeight.w500
);
static const TextStyle c9F11 = TextStyle(
fontSize: 11,
color: Color(0xFF999999),
fontWeight: FontWeight.w400
);
}
part of widgets;
enum CustomImageType { asset, network, file }
enum CustomImageType { asset, network, file, base64 }
class CustomImage extends StatelessWidget {
final String url;
final Widget? placeholder;
final Widget? error;
final CustomImageType type;
final double? width;
final double? height;
final double? radius;
final BoxFit fit;
final Color? backgroundColor;
final ExtendedImageMode mode;
final bool enableSlideOutPage;
final InitGestureConfigHandler? initGestureConfig;
......@@ -31,6 +34,9 @@ class CustomImage extends StatelessWidget {
this.mode = ExtendedImageMode.none,
this.enableSlideOutPage = false,
this.initGestureConfig,
this.backgroundColor,
this.placeholder,
this.error,
}) : super(key: key);
const CustomImage.network({
......@@ -40,6 +46,9 @@ class CustomImage extends StatelessWidget {
this.height,
this.radius,
this.fit = BoxFit.cover,
this.backgroundColor,
this.placeholder,
this.error,
this.builder,
}) : type = CustomImageType.network,
mode = ExtendedImageMode.none,
......@@ -54,6 +63,9 @@ class CustomImage extends StatelessWidget {
this.height,
this.radius,
this.fit = BoxFit.cover,
this.backgroundColor,
this.placeholder,
this.error,
this.builder,
}) : type = CustomImageType.asset,
mode = ExtendedImageMode.none,
......@@ -68,6 +80,9 @@ class CustomImage extends StatelessWidget {
this.height,
this.radius,
this.fit = BoxFit.cover,
this.backgroundColor,
this.placeholder,
this.error,
this.builder,
}) : type = CustomImageType.file,
mode = ExtendedImageMode.none,
......@@ -75,61 +90,158 @@ class CustomImage extends StatelessWidget {
initGestureConfig = null,
super(key: key);
const CustomImage.base64({
Key? key,
required this.url,
this.width,
this.height,
this.radius,
this.fit = BoxFit.cover,
this.backgroundColor,
this.placeholder,
this.error,
this.builder,
}) : type = CustomImageType.base64,
mode = ExtendedImageMode.none,
enableSlideOutPage = false,
initGestureConfig = null,
super(key: key);
@override
Widget build(BuildContext context) {
final borderRadius = BorderRadius.all(Radius.circular(radius ?? 8.w));
final borderRadius = BorderRadius.all(Radius.circular(radius ?? 10));
final Widget errorChild =
error ?? const Icon(Icons.image_not_supported_outlined);
final Widget placeholderChild =
placeholder ?? const CupertinoActivityIndicator(radius: 12);
Widget? image;
switch (type) {
case CustomImageType.asset:
return ExtendedImage.asset(
url,
width: width,
height: height,
fit: fit,
shape: BoxShape.rectangle,
borderRadius: borderRadius,
mode: mode,
enableSlideOutPage: enableSlideOutPage,
initGestureConfigHandler: initGestureConfig,
loadStateChanged: (state) => _buildLoadState(context, state),
);
if (!url.startsWith('assets/')) {
image = errorChild;
} else {
image = ExtendedImage.asset(
url,
key: ValueKey(url),
width: width,
height: height,
fit: fit,
gaplessPlayback: true,
shape: BoxShape.rectangle,
borderRadius: borderRadius,
mode: mode,
enableSlideOutPage: enableSlideOutPage,
initGestureConfigHandler: initGestureConfig,
loadStateChanged: (state) => _buildLoadState(
context: context,
state: state,
placeholder: placeholderChild,
error: errorChild,
),
);
}
break;
case CustomImageType.network:
return ExtendedImage.network(
url,
width: width,
height: height,
fit: fit,
shape: BoxShape.rectangle,
borderRadius: borderRadius,
mode: mode,
enableSlideOutPage: enableSlideOutPage,
initGestureConfigHandler: initGestureConfig,
loadStateChanged: (state) => _buildLoadState(context, state),
);
if (!url.startsWith('http')) {
image = errorChild;
} else {
image = ExtendedImage.network(
url,
key: ValueKey(url),
width: width,
height: height,
fit: fit,
shape: BoxShape.rectangle,
borderRadius: borderRadius,
mode: mode,
gaplessPlayback: true,
enableSlideOutPage: enableSlideOutPage,
initGestureConfigHandler: initGestureConfig,
loadStateChanged: (state) => _buildLoadState(
context: context,
state: state,
placeholder: placeholderChild,
error: errorChild,
),
);
}
break;
case CustomImageType.file:
return ExtendedImage.file(
File(url),
width: width,
height: height,
fit: fit,
shape: BoxShape.rectangle,
borderRadius: borderRadius,
mode: mode,
enableSlideOutPage: enableSlideOutPage,
initGestureConfigHandler: initGestureConfig,
loadStateChanged: (state) => _buildLoadState(context, state),
);
if (!url.startsWith('/')) {
image = errorChild;
} else {
image = ExtendedImage.file(
File(url),
key: ValueKey(url),
width: width,
height: height,
fit: fit,
shape: BoxShape.rectangle,
borderRadius: borderRadius,
mode: mode,
gaplessPlayback: true,
enableSlideOutPage: enableSlideOutPage,
initGestureConfigHandler: initGestureConfig,
loadStateChanged: (state) => _buildLoadState(
context: context,
state: state,
placeholder: placeholderChild,
error: errorChild,
),
);
}
break;
case CustomImageType.base64:
/*if (!url.startsWith('data:image') &&
(placeholder != null || error != null)) {
image = error ?? placeholder;
} else {
final base64 = url.split(',')[1];
final bytes = const Base64Decoder().convert(base64);
image = ExtendedImage.memory(
bytes,
width: width,
height: height,
fit: fit,
shape: BoxShape.rectangle,
borderRadius: borderRadius,
mode: mode,
gaplessPlayback: true,
enableSlideOutPage: enableSlideOutPage,
initGestureConfigHandler: initGestureConfig,
loadStateChanged: (state) => _buildLoadState(context, state),
);
}*/
break;
}
return Container(
clipBehavior: Clip.hardEdge,
width: width,
height: height,
decoration: BoxDecoration(
color: backgroundColor ?? Theme.of(context).colorScheme.tertiary,
borderRadius: borderRadius,
),
child: IconTheme(
data: IconThemeData(
size: 24,
color: Theme.of(context).colorScheme.onTertiary,
),
child: image ?? const SizedBox.shrink(),
),
);
}
Widget _buildLoadState(BuildContext context, ExtendedImageState state) {
Widget _buildLoadState({
required BuildContext context,
required ExtendedImageState state,
required Widget placeholder,
required Widget error,
}) {
switch (state.extendedImageLoadState) {
case LoadState.loading:
return Center(
child: CustomLoadingIndicator(
size: 30.w,
strokeWidth: 3.w,
),
);
return Center(child: placeholder);
case LoadState.completed:
Size? size;
if (state.extendedImageInfo != null) {
......@@ -142,13 +254,7 @@ class CustomImage extends StatelessWidget {
final completed = state.completedWidget;
return builder?.call(context, provider, completed, size) ?? completed;
case LoadState.failed:
return Center(
child: Icon(
Icons.image_not_supported_outlined,
size: 24.w,
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.7),
),
);
return Center(child: error);
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论