CommunityAPP/lib/home_content_page.dart

524 lines
16 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:communityapp/bill.dart';
import 'package:communityapp/emergency.dart';
import 'package:communityapp/package.dart';
import 'package:communityapp/visitor.dart';
import 'package:flutter/material.dart';
import 'reapair.dart';
import 'activity.dart';
import 'announcement.dart';
class NotificationIcon extends StatefulWidget {
const NotificationIcon({super.key});
@override
State<NotificationIcon> createState() => _NotificationIconState();
}
class _NotificationIconState extends State<NotificationIcon> {
List<String> _notifications = [];
int get _unreadCount => _notifications.length;
@override
void initState() {
super.initState();
_fetchNotifications(); // 模擬 API 呼叫
}
void _fetchNotifications() async {
// 模擬從 API 取得資料
// await Future.delayed(const Duration(seconds: 1));
setState(() {
_notifications = ['4/15 管理費已出帳', '4/14 社區電梯保養通知', '4/13 新活動報名開始'];
});
}
@override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
PopupMenuButton<String>(
icon: const Icon(Icons.notifications),
offset: const Offset(0, 40), // 👈 向下移開 40px避免蓋住鈴鐺
onSelected: (value) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('點擊通知:$value')));
},
itemBuilder: (BuildContext context) {
return _notifications.map((String notification) {
return PopupMenuItem<String>(
value: notification,
child: Text(notification),
);
}).toList();
},
),
if (_unreadCount > 0)
Positioned(
right: 4,
top: 4,
child: Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
constraints: const BoxConstraints(minWidth: 16, minHeight: 16),
child: Text(
'$_unreadCount',
style: const TextStyle(color: Colors.white, fontSize: 10),
textAlign: TextAlign.center,
),
),
),
],
);
}
}
class HomeContentPage extends StatelessWidget {
const HomeContentPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('社區通'),
actions: [
Row(
children: [
const Text(
'林小安 您好',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(width: 8),
NotificationIcon(),
/*Stack(
children: [
IconButton(
icon: const Icon(Icons.notifications),
onPressed: () {
// TODO: 跳轉到通知頁
},
),
Positioned(
right: 8,
top: 8,
child: Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(10),
),
constraints: const BoxConstraints(
minWidth: 16,
minHeight: 16,
),
child: const Text(
'3',
style: TextStyle(color: Colors.white, fontSize: 10),
textAlign: TextAlign.center,
),
),
),
],
),*/
],
),
],
),
body: SingleChildScrollView(
child: Column(
children: [
_announcementSection(context),
_quickMenuSection(context),
_adCarousel(),
_marqueeNotice(),
_activitySection(),
],
),
),
);
}
// 📢 重要公告區塊
static Widget _announcementSection(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Text(
'📢 重要公告',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const Spacer(),
TextButton(
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => const AnnouncementWrapper(),
);
},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.blue,
side: const BorderSide(color: Colors.blue), // 藍色外框
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20), // 更小的圓角
),
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
), // 更小的內距
minimumSize: const Size(0, 0), // 取消預設最小尺寸限制
tapTargetSize: MaterialTapTargetSize.shrinkWrap, // 點擊範圍不外擴
),
child: const Text(
'更多',
style: TextStyle(fontSize: 14), // 更小字體
),
),
],
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Card(
child: ListTile(
title: const Text('4/20 水塔清洗通知'),
subtitle: const Text('本週六早上9:00至下午3:00進行清洗請提前儲水。'),
),
),
),
],
),
);
}
// 🔧 功能選單區塊
static Widget _quickMenuSection(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'🔧 功能選單',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
GridView.count(
crossAxisCount: 3,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
childAspectRatio: 1,
children: [
_buildQuickButton(context, '報修', 'assets/icons/repair.png'),
_buildQuickButton(context, '包裹', 'assets/icons/mail.png'),
_buildQuickButton(context, '訪客', 'assets/icons/visitor.png'),
_buildQuickButton(context, '繳費', 'assets/icons/payment.png'),
_buildQuickButton(context, '社區互動', 'assets/icons/community.png'),
_buildQuickButton(context, '緊急通報', 'assets/icons/emergency.png'),
],
),
],
),
);
}
static Widget _buildQuickButton(
BuildContext context,
String title,
String imgAssetPath,
) {
return GestureDetector(
onTap: () {
switch (title) {
case "報修":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => const RepairPageWrapper(),
);
case "包裹":
/*Navigator.push(
context,
MaterialPageRoute(builder: (context) => PackagePage()),
);*/
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => PackagePageWrapper(),
);
case "訪客":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => VisitorPageWrapper(),
);
case "繳費":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => BillPageWrapper(),
);
case "社區互動":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => ActivityListPage(),
);
case "緊急通報":
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (_) => EmergencyPage(),
);
default:
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(imgAssetPath, width: 40, height: 40, color: Colors.grey),
const SizedBox(height: 8),
Text(title, style: const TextStyle(fontSize: 14)),
],
),
);
}
// 🖼️ 廣告輪播區
static Widget _adCarousel() {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: SizedBox(
height: 180,
child: PageView(
children: [
Image.asset('assets/banners/banner1.png', fit: BoxFit.cover),
Image.asset('assets/banners/banner2.png', fit: BoxFit.cover),
Image.asset('assets/banners/banner3.png', fit: BoxFit.cover),
],
),
),
);
}
// 🏃‍♂️ 跑馬燈公告
static Widget _marqueeNotice() {
return const MarqueeNotice();
}
// 🎉 活動卡片列表
static Widget _activitySection() {
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'🎉 最新活動',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
SizedBox(
height: 240,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
_buildActivityCard(
'中秋烤肉趴',
'9/9 晚上6點開烤',
'assets/activities/bbq.png',
),
_buildActivityCard(
'親子日遊園',
'玩具福袋等你拿!',
'assets/activities/family_day.png',
),
_buildActivityCard(
'健康講堂',
'醫師到場解說',
'assets/activities/health.png',
),
_buildActivityCard(
'防災演習',
'模擬火災逃生',
'assets/activities/fire_drill.png',
),
_buildActivityCard(
'社區園遊會',
'免費攤位吃到飽',
'assets/activities/festival.png',
),
],
),
),
],
),
);
}
static Widget _buildActivityCard(
String title,
String subtitle,
String imgAssetPath,
) {
return Container(
width: 180,
margin: const EdgeInsets.only(right: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 5)],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(10)),
child: Image.asset(
imgAssetPath,
height: 120,
width: double.infinity,
fit: BoxFit.cover,
),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
Text(
subtitle,
style: const TextStyle(fontSize: 12, color: Colors.black54),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.start, // 左對齊或你要調整的位置
children: [
ElevatedButton(
onPressed: () {
// TODO: 報名事件
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green, // 綠色
fixedSize: const Size(75, 30), // 寬高都 60正方形
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // 0 圓角
),
),
child: const Text(
'報名',
softWrap: false,
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
const SizedBox(width: 10), // 按鈕間隔
ElevatedButton(
onPressed: () {
// TODO: 查看活動
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // 藍色
fixedSize: const Size(75, 30), // 寬高都 60正方形
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8), // 0 圓角
),
),
child: const Text(
'查看',
softWrap: false,
style: TextStyle(color: Colors.white, fontSize: 12),
),
),
],
),
],
),
),
],
),
);
}
}
class MarqueeNotice extends StatefulWidget {
const MarqueeNotice({super.key});
@override
State<MarqueeNotice> createState() => _MarqueeNoticeState();
}
class _MarqueeNoticeState extends State<MarqueeNotice>
with SingleTickerProviderStateMixin {
late final AnimationController _controller;
late final Animation<double> _animation;
final String _marqueeText = '公告: 歡迎使用我們的社區通,請多加利用。謝謝您~ 如有問題可洽談管理室';
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 10),
vsync: this,
)..repeat();
_animation = Tween<double>(begin: 1.0, end: -1.0).animate(_controller);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey.shade300,
height: 40,
alignment: Alignment.centerLeft,
child: ClipRect(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return FractionalTranslation(
translation: Offset(_animation.value, 0),
child: child,
);
},
child: Text(
_marqueeText,
style: const TextStyle(fontSize: 16),
overflow: TextOverflow.visible,
softWrap: false,
),
),
),
);
}
}