Attendance and staff management application for Ma'had Tahfizh al-Qur'an al-Imam as-Syathiby.
Enhanced and customized version of Syathiby Vendor App β https://github.com/creatorb/flutter-syathiby-vendor
π Changelog: See CHANGELOG.md for full version history.
- Quick Start
- Prerequisites
- Installation
- URL Environment Setup
- Color Palette & Theme
- Development β Web Dev Cheatsheet
- Building & Deployment
- Features
- Environment Indicators
- Testing
- Troubleshooting
- Keystore
- Advanced Configuration
- Project Structure
- Contributing
- Branches
- Team & Contact
- License
- QUICK_REFERENCE.md - One-page quick reference (print-friendly!)
- WEB_DEV_CHEATSHEET.md - Quick reference for web development commands
- WEB_DEPLOYMENT_GUIDE.md - Detailed deployment troubleshooting & checklist
- CHANGELOG.md - Version history and feature changes
# 1. Clone repository
git clone https://github.com/creatorb/flutter_syathiby.git
cd flutter_syathiby
# 2. Install dependencies
fvm flutter pub get
# 3. Generate code
fvm flutter pub run build_runner build --delete-conflicting-outputs
# 4. Run app (PROD flavor β automatically uses production URL)
fvm flutter run --flavor prod --dart-define=FLAVOR=prod
# 5. Run app (LOCAL flavor β automatically uses local URL)
fvm flutter run --flavor local --dart-define=FLAVOR=local- Flutter SDK: Managed via FVM
- Android Studio / Xcode (for Android/iOS builds)
- ADB (Android Debug Bridge) for device deployment
- Git
# Windows (PowerShell)
choco install fvm
# macOS
brew tap leoafarias/fvm
brew install fvm
# Linux / Manual
dart pub global activate fvm# Install the Flutter version used by this project
fvm install
# Activate FVM version
fvm usefvm flutter pub getfvm flutter pub run build_runner build --delete-conflicting-outputsSee the Keystore section below.
The app uses automatic flavor-based URL configuration. The backend URL is selected automatically based on the active flavor:
| Flavor | API URL | Link Base | App Name |
|---|---|---|---|
| prod | https://aplikasi.syathiby.id/geten/ |
https://aplikasi.syathiby.id |
Syathiby |
| local | http://192.168.50.100/aplikasi/geten/ |
http://192.168.50.100/aplikasi |
Syathiby LOCAL |
Production:
fvm flutter run --flavor prod --dart-define=FLAVOR=prodβ
Automatically uses: https://aplikasi.syathiby.id
Local Development:
fvm flutter run --flavor local --dart-define=FLAVOR=localβ
Automatically uses: http://192.168.50.100/aplikasi
Note:
--dart-define=FLAVOR=xxxtells the app which flavor is active so the correct URL is selected automatically.
If your local server IP is not 192.168.50.100, edit:
π lib/res/flavor_config.dart
static const Map<String, Map<String, String>> _configs = {
'prod': {
'API_URL': 'https://aplikasi.syathiby.id/geten/',
'LINK_BASE': 'https://aplikasi.syathiby.id',
},
'local': {
'API_URL': 'http://192.168.1.100/aplikasi/geten/', // β Change IP here
'LINK_BASE': 'http://192.168.1.100/aplikasi', // β and here
},
};After editing, rebuild:
fvm flutter clean
fvm flutter pub get
fvm flutter run --flavor local --dart-define=FLAVOR=localYou can also temporarily override the URL at runtime without rebuilding:
- Launch the app (any flavor)
- On the Login screen, long-press the Syathiby logo
- A dialog appears showing the active URL
- Enter your custom URL:
API URL: http://192.168.1.200/aplikasi/geten/ Link Base: http://192.168.1.200/aplikasi - Tap "Save & Restart"
The override is stored in SharedPreferences and persists until manually reset.
- Long-press the logo on the Login screen
- Clear all fields
- Tap "Save & Restart"
The app will revert to the default URL for the active flavor.
Find your server's local IP:
Windows:
ipconfigmacOS / Linux:
ifconfig
# or
ip addr showLook for an IP starting with 192.168.x.x or 10.x.x.x.
Environment detection:
The app automatically detects the environment based on the active URL:
- Local IP (
192.168.x.x,10.x.x.x,172.16β31.x.x,localhost) β LOCAL (red banner) - Other public domain β PROD (green banner, hidden in release)
flutter_syathiby/
βββ lib/res/
βββ flavor_config.dart # URL configuration per flavor
βββ environment_config.dart # Runtime config + URL override logic
βββ env.dart # Legacy (optional, for MAPS_API_KEY)
Issue: URL still wrong after changing flavor
# Make sure to pass --dart-define=FLAVOR
fvm flutter run --flavor local --dart-define=FLAVOR=local
# β οΈ NOT: fvm flutter run --flavor local (missing --dart-define)Issue: Cannot connect to local server
- β Verify the backend server is running
- β Device and server are on the same network (same Wi-Fi)
- β
Test the URL in a browser:
http://192.168.x.x/aplikasi/geten/ - β Check firewall is not blocking the connection
- β
Use the device's IP address, not
localhost
Issue: Environment label shows PROD instead of LOCAL
- Long-press the logo to verify the actual URL in use
- Ensure the URL uses
http://(nothttps://) for local - Confirm the IP matches the local pattern:
192.168.x.x
The app uses a custom green color palette with a modern 3D gradient design.
| Role | Hex Code | Color Preview |
|---|---|---|
| Primary | #26774e |
|
| Dark | #19633f |
|
| Light | #82aa68 |
β¨ Modern 3D Gradient Design:
- Strong gradient colors using base color palette (Dark β Primary β Light)
- 3D elevation effects with deep shadows (elevation 8-16)
- Bold visual depth with multi-layer shadows
- Material 3 design system with enhanced depth perception
- Gradient accents on AppBar, buttons, cards, and navigation
- Support for dark and light modes with adaptive gradients
- Rounded corners (16-28px) and generous padding
- High-contrast color scheme for better visibility
Key Design Elements:
- π¨ AppBar: Gradient background with 8px elevation
- π³ Cards: Strong 3D shadow with 8-10px elevation
- π Buttons: Bold gradient colors with 8-12px elevation
- π± Navigation Bar: Gradient indicators with 12px elevation
- π― Inputs: Gradient focus borders (2.5px)
- β¨ Dialogs & Sheets: Deep shadows with 16px elevation
π― Elegant 3D Menu Icons: All grid menu icons throughout the app feature an elegant 3D design:
- Multi-layer shadows: 3 shadow layers for realistic depth perception
- Top-left highlight (simulated light source)
- Main shadow (bottom-right)
- Additional depth shadow (deeper)
- Gradient backgrounds: Smooth color transitions from light to dark
- Icon shadows: Individual icon elements have subtle shadows
- Text shadows: Labels feature depth shadows
- Rounded corners: 20px border radius for smooth, modern look
- Border highlights: Semi-transparent borders for edge definition
- Compact size: 58x58px menu icon containers for cleaner layout density
Applied in:
- Home screen menu grids (all menu categories)
- Prayer/Ibadah screen icons
- Book selection grid
- All other menu navigation elements
The main attendance CTA (Absen Masuk / Absen Pulang) now uses a compact, premium interaction model:
- Compact geometry: 44px rendered height on Home for a tighter, modern look
- Fast premium tap animation: quick bounce timing tuned for responsiveness
- Fetch-aware loading state: inline spinner + dynamic label (
Absen masuk.../Absen pulang...) - Tap lock during processing: prevents accidental double-submit while status is updating
- Smooth state handoff: waits for refreshed presence/profile data before returning to idle
π¦ Custom 3D Components: The app includes reusable widgets for consistent 3D styling:
Elegant3DIcon - Beautiful 3D icon container
- Multi-layer shadows with adjustable depth
- Gradient backgrounds
- Customizable size and colors
- Icon shadow effects
Elegant3DCard - 3D elevated card widget
- Multi-layer shadow system
- Optional gradient backgrounds
- Customizable borders and corners
- Adjustable depth intensity (0.0 to 1.0)
Elegant3DButton - compact premium action button
- Fast bounce animation on press
- Built-in loading mode (
isLoading) with inline progress indicator - Dynamic loading label support (
loadingLabel) - Designed for primary CTAs like attendance check-in/check-out
Usage Example:
// Simple 3D icon
Elegant3DIcon(
iconData: Icons.home,
size: 58,
iconSize: 24,
)
// Interactive 3D icon button
Elegant3DIconButton(
iconData: Icons.settings,
onTap: () { /* action */ },
depth: 0.72,
)
// Compact attendance button with loading state
Elegant3DButton(
label: 'Absen Masuk',
icon: Icons.check_circle,
height: 44,
depth: 0.62,
isLoading: false,
loadingLabel: 'Absen masuk...',
onPressed: () { /* action */ },
)
// 3D card with gradient
Elegant3DCard(
borderRadius: 20,
useGradient: true,
child: YourContent(),
)π Theme Configuration Files:
lib/app.dart- Main theme setup with 3D gradient stylinglib/res/colors.dart- Color scheme definitionslib/utils/extension/color.dart- Color extensionslib/presentation/home/home_screen.dart- 3D icon grid implementationlib/presentation/widgets/elegant_3d_icon.dart- Reusable 3D icon widgetlib/presentation/widgets/elegant_3d_button.dart- Reusable compact 3D action buttonlib/presentation/widgets/elegant_3d_card.dart- Reusable 3D card widget
To modify the theme:
- Edit the color values in
lib/res/colors.dart - Update theme configuration in
lib/app.dart - Adjust 3D icon effects in
buildListMenu()method or use reusable widgets - Customize
Elegant3DIconandElegant3DCarddepth parameter (0.0 - 1.0) - Rebuild the app with
fvm flutter run
# PROD flavor (automatically uses https://aplikasi.syathiby.id)
fvm flutter run --flavor prod --dart-define=FLAVOR=prod
# LOCAL flavor (automatically uses http://192.168.50.100/aplikasi)
fvm flutter run --flavor local --dart-define=FLAVOR=local
# Specify device
fvm flutter run -d <device-id> --flavor prod --dart-define=FLAVOR=prod
fvm flutter run -d 127.0.0.1:5555 --flavor local --dart-define=FLAVOR=local- Hot Reload:
r(in terminal while app is running) - Hot Restart:
R - Quit:
q
Run Flutter web version locally for development and debugging:
# Simulates production environment (uses production API)
fvm flutter run -d chrome- URL:
http://localhost:8080 - API:
https://aplikasi.syathiby.id(production) - Best for: Final testing before deployment
# Development environment (uses local backend API)
fvm flutter run -d chrome --web-hostname 192.168.50.100 --web-port 8082- URL:
http://192.168.50.100:8082 - API:
http://192.168.50.100/aplikasi(local) - Hot reload: Enabled (press
rfor instant reload) - Best for: Debugging with local backend
# Starts PROD and LOCAL servers in separate browser windows
.\run-web-both.ps1
# PROD: http://localhost:8080
# LOCAL: http://192.168.50.100:8082See WEB_DEV_CHEATSHEET.md for more web development tips & tricks!
Run whenever you modify models or environment config:
fvm flutter pub run build_runner build --delete-conflicting-outputsWatch mode (auto-generates on file changes):
fvm flutter pub run build_runner watch --delete-conflicting-outputsIf you encounter cache or dependency issues:
fvm flutter clean
fvm flutter pub get
fvm flutter pub run build_runner build --delete-conflicting-outputs# PROD flavor
fvm flutter build apk --debug --flavor prod --dart-define=FLAVOR=prod
# LOCAL flavor
fvm flutter build apk --debug --flavor local --dart-define=FLAVOR=localOutput: build/app/outputs/flutter-apk/
# PROD flavor
fvm flutter clean
fvm flutter pub get
fvm flutter pub run build_runner build --delete-conflicting-outputs
fvm flutter build apk --release --flavor prod --dart-define=FLAVOR=prod
# LOCAL flavor
fvm flutter clean
fvm flutter pub get
fvm flutter pub run build_runner build --delete-conflicting-outputs
fvm flutter build apk --release --flavor local --dart-define=FLAVOR=local# PROD
fvm flutter clean
fvm flutter pub get
fvm flutter pub run build_runner build --delete-conflicting-outputs
fvm flutter build appbundle --release --flavor prod --dart-define=FLAVOR=prod
# LOCAL
fvm flutter clean
fvm flutter pub get
fvm flutter pub run build_runner build --delete-conflicting-outputs
fvm flutter build appbundle --release --flavor local --dart-define=FLAVOR=localOutput: build/app/outputs/bundle/
Use the PowerShell script to install PROD and LOCAL simultaneously:
.\install-both-apks.ps1Requirements:
- Android device connected via USB or Wi-Fi
- ADB installed and available in PATH
- Both APKs already built (see above)
The script will:
- Validate ADB availability
- Check device connection
- Install
app-local-debug.apkβid.syathiby.app.local - Install
app-prod-debug.apkβid.syathiby.app - Display colored status output (green = success, red = failed)
Production build (deployed to aplikasi.syathiby.id/web/):
# Manual build (step by step)
fvm flutter clean
fvm flutter pub get
fvm flutter pub run build_runner build --delete-conflicting-outputs
fvm flutter build web --release --base-href /web/ --tree-shake-icons
β οΈ IMPORTANT: Flag--base-href /web/is REQUIRED because Flutter web is deployed as a subfolder/web/ataplikasi.syathiby.id.
OR use automated deployment script (recommended):
# Full build + auto-copy to ../aplikasi/web/ + rename htaccess -> .htaccess
.\deploy-web.ps1
# Skip clean step (faster for minor changes)
.\deploy-web.ps1 -SkipCleanThe script will:
- Clean, get dependencies, generate code
- Build web with
--base-href /web/ - Verify critical files (
htaccess,flutter_bootstrap.js,main.dart.js,version.json) - Prompt to auto-copy build output to
../aplikasi/web/(with auto-renamehtaccessβ.htaccess)
Output: build/web/
β οΈ CRITICAL: Web Deployment
- Upload contents of
build/web/toweb/folder on serveraplikasi.syathiby.id- DELETE old files in
web/folder before uploading- Clear server cache (Cloudflare/cPanel/Nginx)
- Verify:
https://aplikasi.syathiby.id/web/version.jsonSee comprehensive guide: WEB_DEPLOYMENT_GUIDE.md
Test the built web app locally before uploading to production.
Recommended: Laragon Virtual Host
Laragon auto-creates a virtual host for every folder in C:/laragon/www/. The aplikasi folder becomes aplikasi.test:
http://aplikasi.test/web/ β same path as production, works with --base-href /web/
No extra configuration needed β just run the deploy script with auto-copy, then open http://aplikasi.test/web/.
Alternative: localhost/aplikasi/web/ via .htaccess Rewrite
If you prefer localhost/aplikasi/web/, create C:/laragon/www/.htaccess:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/web/
RewriteCond %{REQUEST_URI} !^/aplikasi/
RewriteRule ^web/(.*)$ /aplikasi/web/$1 [L]
</IfModule>This rewrites localhost/web/* β localhost/aplikasi/web/* so Flutter can find its assets.
A pre-made template is available at
aplikasi/web/htaccess_xampp_rootβ copy and rename to.htaccessin your Laragon/XAMPP www root.
Local testing workflow:
1. .\deploy-web.ps1 β build + auto-copy to aplikasi/web/
2. Open http://aplikasi.test/web/ β test (should match native app)
3. Confirmed OK β upload aplikasi/web/ to server
Note: Web version includes Guest Mode with News, Prayer Schedule, and Quran features accessible without login. The app automatically starts in guest mode at
/guest-newswhen no session exists.
Web Architecture (Same-Origin, No CORS):
aplikasi.syathiby.id/ -> redirect to /web/
aplikasi.syathiby.id/web/ -> Flutter web app (subfolder)
aplikasi.syathiby.id/geten/ -> Backend API
aplikasi.syathiby.id/wordpress_images.php -> Image proxy
Flutter web and backend run on the same origin (aplikasi.syathiby.id), so there are no CORS issues.
Problem: Unlike native apps that show update dialogs from Play Store, web users don't know when new features are deployed because files are replaced instantly.
Solution: Web-specific changelog notification system using localStorage version tracking:
- Automatic detection: App checks current version vs last seen version on home screen load
- First-time visitors: See changelog modal for current version on first visit
- After updates: When deployment replaces files with new version, modal appears automatically
- Content: Changelog fetched from GitHub CHANGELOG.md (same as native)
- User control: "Understood" action closes the modal and marks the version as seen
- Storage: Uses browser localStorage to persist last seen version
Implementation:
UpdateChecker.checkForWeb(): Check if changelog should be shown (web only)UpdateChecker.markChangelogAsSeen(): Save current version to localStorage- Modal shows "What's New" instead of "Update Available"
- No Play Store button on web (replaced with an "Understood" acknowledgment)
Benefits:
- Web users always informed about new features after deployment
- Consistent changelog experience across native and web platforms
- No manual user action required (automatic detection)
- Non-intrusive (appears once per version, can be dismissed)
After deployment:
- Open incognito/private window (Ctrl+Shift+N) β old service worker won't interfere
- Test guest mode: Should land on News page automatically
- Test login: Click "Pengguna" tab β "Masuk" button
- Verify icons and UI match the native APK β if different, the
.htaccesscache headers may not be active
Why icons/UI may appear outdated after rebuild:
Flutter web uses a service worker (flutter_service_worker.js) to cache assets. If this file is cached by the browser, the old service worker keeps serving old assets even after you rebuild and upload.
The .htaccess in web/ includes Cache-Control: no-store for critical files:
flutter_service_worker.jsβ most important: must always be freshflutter_bootstrap.js,index.html,version.json,manifest.json
Static assets (.js, .wasm, fonts, images) use long-term caching β the service worker handles invalidation via content hash manifests.
# Run production build on Chrome (default: localhost:8080)
fvm flutter run -d chrome
# Or specify production server URL
fvm flutter run -d chrome --web-hostname localhost --web-port 8080Features:
- Uses production API URL:
https://aplikasi.syathiby.id - No banner (clean production UI)
- Resembles deployed version
- Best for final testing before deployment
# Run local build on Chrome with custom hostname/port
fvm flutter run -d chrome --web-hostname 192.168.50.100 --web-port 8082
# Or if using localhost (same machine)
fvm flutter run -d chrome --web-hostname localhost --web-port 8082Configuration:
- Change
192.168.50.100to your machine's IP address - Change
8082to any available port - Requires local backend server running at
http://192.168.50.100/aplikasi
Features:
- Uses local/development API URL
- Shows RED banner with "LOCAL" indicator
- Hot reload enabled (press
rin terminal) - Perfect for debugging and development
- Preserves app state on refresh
# Start development server with full debugging
fvm flutter run -d chrome --web-hostname 192.168.50.100 --web-port 8082 -v
# After app loads, press 'r' for hot reload
# Press 'q' to quitDevelopment Workflow:
- Run command above
- Browser opens automatically at
http://192.168.50.100:8082 - Edit Dart code in IDE
- Press
rin terminal β app reloads instantly - See changes immediately
- Check DevTools for logs (
http://localhost:9222in new browser tab)
# Run with debugging support
fvm flutter run -d chrome --web-hostname 192.168.50.100 --web-port 8082 -v
# Open new Chrome tab and go to:
chrome://inspect/#devices
# Click "inspect" on the Flutter appAvailable in DevTools:
- Widget tree explorer
- Performance profiler
- Network requests
- Console logs
- Breakpoints & step debugging
# Run PROD environment
Write-Host "Starting PROD server..." -ForegroundColor Green
Start-Process -NoNewWindow pwsh -ArgumentList @"-NoExit", "-Command", "fvm flutter run -d chrome --web-hostname localhost --web-port 8080"
# Wait a moment for server to start
Start-Sleep -Seconds 3
# Run LOCAL environment in another terminal
Write-Host "Starting LOCAL server..." -ForegroundColor Yellow
Start-Process -NoNewWindow pwsh -ArgumentList @"-NoExit", "-Command", "fvm flutter run -d chrome --web-hostname 192.168.50.100 --web-port 8082"
Write-Host ""
Write-Host "β PROD: http://localhost:8080" -ForegroundColor Green
Write-Host "β LOCAL: http://192.168.50.100:8082" -ForegroundColor YellowSave as run-web-both.ps1 and run: .\run-web-both.ps1
Troubleshooting web routing:
- If landing on login instead of guest mode: Clear browser localStorage
- Open DevTools β Application β Local Storage β Delete all entries
- Refresh page β Should redirect to guest news
- Use incognito/private mode to avoid cached assets
| Feature | APK (Mobile) | Web (Browser) |
|---|---|---|
| Push Notifications | β Full FCM support | β Disabled |
| GPS Location | β Native GPS | |
| Biometric Auth | β Fingerprint/Face | β Not available |
| Camera/QR Scanner | β Native camera | |
| Offline Mode | β Local caching | β Requires internet |
| Background Services | β Supported | β Not available |
| File System | β Full access | |
| Guest Mode | β Full support | β Full support |
| WordPress News | β Full support | β Full support |
| Attendance | β GPS + Wi-Fi | |
| Login/Auth | β Full support | β Full support |
| Reports & Analytics | β Full support | β Full support |
- APK: Firebase Cloud Messaging with background notifications, notification channels, and message handling
- Web: Completely disabled. Firebase initialization is skipped on web for Safari compatibility
- Impact: Web users won't receive real-time notifications about attendance, announcements, or updates
- APK:
- Native Android location services with high accuracy
- Permission dialogs with "Open Settings" direct link
- Wi-Fi attendance with IP validation
- GPS-based attendance with radius validation
- Web:
- Browser Geolocation API (accuracy varies by device/browser)
- Permission dialogs show an acknowledgment-only action ("Mengerti")
- Users must grant location manually via browser settings
- Wi-Fi attendance works via IP detection from server headers
- Impact: Web attendance may have lower GPS accuracy; users need to manually enable browser location
- APK: Local Auth plugin supports fingerprint and face unlock
- Web: Not implemented (no Web Authentication API integration)
- Impact: Web users can only use password authentication
- APK:
- Barcode/QR scanner for attendance, meetings, inventory
- Full camera access for photo capture
- Local file system read/write
- Background service for location tracking
- Web:
- Browser camera API (may require HTTPS)
- File downloads only, no direct filesystem
- No background services
- QR scanner works but limited by browser camera quality
- Impact: QR scanning and photo features work better on APK
- APK: Local database caching allows viewing previously loaded data offline
- Web: Requires active internet connection for all operations
- Impact: APK more reliable in areas with poor connectivity
- APK: Native UI with smooth animations, system integration, navigation gestures
- Web: Responsive design that adapts to screen size, works on any device with browser
- Impact: APK feels more native, Web more accessible cross-platform
Both platforms support:
- β Guest Mode: News, Prayer Times, Quran, Qibla
- β Authentication: Login, password change, session management
- β WordPress News: Full news feed with images and rich content
- β Attendance: Check-in/out (with platform-specific location handling)
- β Staff Management: View and manage staff data
- β Reports: Attendance reports, performance tracking, analytics
- β Dark/Light Theme: Adaptive theme switching
- Web:
WpPostDetailScreenloads the article URL directly from WordPress response (post.link) via WebView URL request. - Android/iOS (APK):
WpPostDetailScreenkeeps the previous rich HTML rendering flow (initialData) for stable native behavior. - Fallback: If
post.linkis missing/invalid on web, the screen falls back to HTML rendering.
This split approach keeps native behavior unchanged while avoiding black/blank embed rendering issues on web.
Use APK when:
- Staff needs daily attendance check-in/out
- Push notifications are required
- Working in areas with intermittent internet
- Need QR scanner for meetings/events
- Prefer native app experience
Use Web when:
- Occasional access to view information
- No access to Play Store (restricted devices)
- Need quick access from any device/computer
- Only need to view reports and data
- Don't need push notifications
Hybrid Approach:
- Give field staff APK for daily use
- Use Web for management/admin dashboard access
- Both platforms share same backend API and data
The app displays news and announcements from the Ma'had Syathiby WordPress site:
- WordPress REST API: Pull latest posts from
https://syathiby.id/wp-json/wp/v2/posts - SEO-Optimized Images: Uses Yoast SEO og_image for fast-loading, optimized thumbnails
- Rich Content Display: Full HTML rendering with InAppWebView
- Embedded Media Support: YouTube videos, Instagram posts, Twitter embeds automatically rendered
- Skeleton Loading: Smooth loading animations while fetching content
- WebP Image Support: Native support for modern WebP format images
- Available in: Guest News screen and Member News screen
How it works:
- News content is managed through WordPress CMS at syathiby.id
- App fetches posts via REST API with embedded featured media
- Images prioritize Yoast SEO optimized thumbnails from
yoast_head_json - Detail screen uses WebView for rich content including videos and embeds
- HTML entities automatically decoded for proper text display
The app supports two attendance methods:
- Location (GPS): Validates attendance based on GPS coordinates and location radius
- Network (Wi-Fi / LAN): Validates attendance using the device's public IP address
How Wi-Fi attendance works:
- User selects the attendance method when tapping the check-in/out button
- Wi-Fi mode sends coordinates
(0, 0)as a mode indicator to the backend - The app fetches the allowed public IP from the server endpoint (
settings/wificonfig.php) - The app validates the device's current public IP against the allowed IP via an external IP API
- If validation passes, the attendance request is submitted β backend skips GPS radius check for
(0, 0)coordinates - Error code
03is used for Wi-Fi validation failures
- Automatically checks for a new version on app startup (Home screen)
- Compares current app version with the latest version in CHANGELOG.md on GitHub
- Dynamic Branch Selection: Selects the appropriate GitHub branch based on current flavor:
- LOCAL flavor (
--flavor local): Fetches CHANGELOG fromtestbranch for development updates - PROD flavor (
--flavor prod): Fetches CHANGELOG fromdevbranch for stable releases
- LOCAL flavor (
- Displays a bottom sheet modal with the changelog content when an update is available
- Provides a direct link to the Play Store for the update
- Non-blocking: users can dismiss and continue using the current version
View and manage staff profile information.
Submit and track leave/permit requests.
View attendance history and activity timeline.
The app provides visual indicators to distinguish between environments:
- LOCAL: Red ribbon with "LOCAL" label (development indicator)
- PROD: No banner β clean UI for end users
Production builds have a clean UI without any environment banner.
- Local environment: "Syathiby LOCAL"
- Production environment: "Syathiby"
- Displays the active ENV label and API URL
- Red for LOCAL, green for PROD
- Long-press logo to override the API URL at runtime
Flavor prod |
Flavor local |
|
|---|---|---|
| Application ID | id.syathiby.app |
id.syathiby.app.local |
| App Label | Syathiby | Syathiby LOCAL |
| Banner | β None (clean UI) | π΄ Red ribbon |
Both flavors can be installed simultaneously on the same device.
A detailed test checklist for Wi-Fi attendance is available at:
π ATTENDANCE_WIFI_TEST_CHECKLIST.md
fvm flutter analyzefvm flutter testThe app fetches news from the WordPress site using the REST API:
Base URL: https://syathiby.id/wp-json/wp/v2
Posts Endpoint: /posts?_embed=true
Query Parameters:
_embed=true: Includes embedded resources (featured media, author, etc.)page=1: Page number for paginationper_page=10: Number of posts per page (default: 10, max: 100)orderby=date: Sort by date, relevance, id, etc.order=desc: Descending order (newest first)
The app uses Freezed models for WordPress data:
WpPost - Main post model
id,date,slug,linktitle,content,excerpt(rendered HTML)featured_media(media ID)_embedded(embedded resources)yoast_head_json(Yoast SEO metadata)
YoastHeadJson - SEO metadata from Yoast plugin
og_image[]- Open Graph images (optimized thumbnails)
OgImage - SEO-optimized image
url: Direct image URL (WebP format)width,height: Image dimensionstype: MIME type (image/webp)
WpEmbedded - Embedded resources
wp:featuredmedia[]- Featured media with source_url
The app uses a prioritized fallback strategy for images:
-
Primary: Yoast SEO og_image
yoast_head_json.og_image[0].urlβ SEO-optimized, pre-resized WebP thumbnails
-
Fallback: Featured media from embedded data
_embedded['wp:featuredmedia'][0].source_url
β οΈ Full-resolution original image
Location: lib/models/wordpress/wp_api_service.dart
@RestApi(baseUrl: 'https://syathiby.id/wp-json/wp/v2')
abstract class WpApiService {
factory WpApiService(Dio dio, {String baseUrl}) = _WpApiService;
@GET('/posts')
Future<List<WpPost>> getPosts({
@Query('page') int? page,
@Query('per_page') int? perPage,
@Query('_embed') bool? embed,
});
@GET('/posts/{id}')
Future<WpPost> getPost(@Path('id') int id);
}Dependency Injection: lib/di/providers.dart
final wpApiServiceProvider = Provider<WpApiService>((ref) {
final dio = ref.watch(dioProvider);
return WpApiService(dio);
});List View (wp_post_list_item.dart):
- Featured image (200px height)
- Title (HTML decoded)
- Excerpt (HTML stripped and decoded)
- Skeleton loading animation
Detail View (wp_post_detail_screen.dart):
- Featured image at top
- Full HTML content rendering with InAppWebView
- Embedded media support (YouTube, Instagram, etc.)
- Custom CSS for responsive layout
The WordPress site uses Yoast SEO plugin which provides:
- Optimized og_image thumbnails (WebP format)
- SEO metadata in
yoast_head_jsonfield - Better performance with pre-resized images
To check if Yoast is installed:
curl https://syathiby.id/wp-json/wp/v2/posts?per_page=1 | jq '.[0].yoast_head_json'fvm flutter pub run build_runner build --delete-conflicting-outputsClear Gradle cache:
# Windows
Remove-Item -Recurse -Force $env:USERPROFILE\.gradle\caches
# macOS/Linux
rm -rf ~/.gradle/cachesThen rebuild:
fvm flutter clean
fvm flutter pub get# Check connected devices
adb devices
# Restart ADB server
adb kill-server
adb start-server
# Connect via Wi-Fi (after USB pairing)
adb tcpip 5555
adb connect <device-ip>:5555Make sure FVM is added to PATH:
# Check FVM installation
fvm --version
# Install FVM if missing
dart pub global activate fvm
# Add to PATH (Windows example)
# Add: %USERPROFILE%\AppData\Local\Pub\Cache\binGenerate a debug keystore for development:
keytool -genkeypair -v `
-keystore debug.keystore `
-alias androiddebugkey `
-keyalg RSA -keysize 2048 `
-validity 10000 `
-storetype pkcs12 `
-storepass android `
-keypass android `
-dname "CN=https://github.com/CreatorB, O=Freelance Fullstack Developer, C=ID"For production builds, use a secure keystore. Store it in a safe location and never commit it to git.
dart run flutter_application_id:main -f flutter_application_id.yaml# Generate configuration
dart run flutter_launcher_icons:generate --override
# Generate icons
dart run flutter_launcher_iconsdart run flutter_native_splash:createflutter_syathiby/
βββ android/ # Android native code & flavor config
βββ ios/ # iOS native code
βββ lib/
β βββ app.dart # Root MaterialApp with environment banner
β βββ data/ # Data layer (repositories, local storage)
β βββ models/
β β βββ wordpress/ # WordPress REST API models
β β β βββ wp_api_service.dart # Retrofit API service
β β β βββ wp_post.dart # WpPost model (Freezed + JSON)
β β βββ ... # Other models
β βββ presentation/
β β βββ home/ # Home screen with update checker
β β βββ login/ # Login screen with environment badge
β β βββ presence/ # Attendance screen with Wi-Fi option
β β βββ wordpress/ # WordPress news screens
β β β βββ wp_posts_controller.dart # Riverpod controller
β β β βββ wp_post_list_item.dart # News list item widget
β β β βββ wp_post_detail_screen.dart # Article detail (WebView)
β β βββ guest/ # Guest screens (uses WordPress API)
β β βββ news/ # Member news (uses WordPress API)
β βββ di/
β β βββ providers.dart # Riverpod providers & DI
β βββ res/
β β βββ flavor_config.dart # Built-in URL configuration per flavor
β β βββ environment_config.dart # Runtime config + URL override
β β βββ strings.dart # App constants, dynamic app name
β βββ utils/
β βββ update_checker.dart # GitHub CHANGELOG version check
βββ test/ # Unit & widget tests
βββ build/ # Build outputs (gitignored)
βββ web/
β βββ htaccess # Apache config: SPA routing + Cache-Control headers
β βββ index.html # Flutter web entry point
β βββ ...
βββ CHANGELOG.md # Version history
βββ ATTENDANCE_WIFI_TEST_CHECKLIST.md # Wi-Fi attendance test guide
βββ WEB_DEPLOYMENT_GUIDE.md # Web deployment checklist & troubleshooting
βββ install-both-apks.ps1 # Dual APK installer script
βββ deploy-web.ps1 # Web build + auto-copy + verification script
βββ pubspec.yaml # Flutter dependencies
βββ README.md # This file
- Create a new branch from
dev - Implement features or fixes with clear commit messages
- Update CHANGELOG.md with a new entry
- Test all changes thoroughly
- Open a Pull Request to the
devbranch
feat: add feature X
fix: resolve bug Y
docs: update documentation for Z
chore: update dependencies
refactor: restructure code for A
Active development branch β continuously updated with the latest features and requirements.
Stable branch. Maintained to support the latest production requirements with original features intact.
Ma'had Tahfizh al-Qur'an al-Imam as-Syathiby
Cileungsi, Bogor, Indonesia
- Website: syathiby.com
- Tags:
pondokjabodetabekma'hadtahfizhal-Qur'ansunnahmanhajsalafcileungsibogorindonesiasyathiby
Copyright IT Syathiby 2024
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.