flutter_homescreen_widget 0.1.5
flutter_homescreen_widget: ^0.1.5 copied to clipboard
Update iOS WidgetKit and Android Glance widgets using Flutter as the UI. Pure Dart — no Swift or Kotlin required. Supports tap actions and multiple sizes.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_homescreen_widget/flutter_homescreen_widget.dart';
final _navigatorKey = GlobalKey<NavigatorState>();
void main() {
FlutterHomescreenWidget.init(_navigatorKey);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: _navigatorKey,
title: 'flutter_homescreen_widget example',
debugShowCheckedModeBanner: false,
theme: ThemeData.dark(),
home: const ClockPage(),
);
}
}
class ClockPage extends StatefulWidget {
const ClockPage({super.key});
@override
State<ClockPage> createState() => _ClockPageState();
}
class _ClockPageState extends State<ClockPage> {
DateTime _now = DateTime.now();
Timer? _timer;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
await Future.delayed(const Duration(milliseconds: 300));
_updateWidgets();
});
_timer = Timer.periodic(const Duration(minutes: 1), (_) {
setState(() => _now = DateTime.now());
_updateWidgets();
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
Future<void> _updateWidgets() async {
final now = DateTime.now();
setState(() => _now = now);
try {
await FlutterHomescreenWidget.update(
widgetName: 'ClockWidget',
size: const Size(329, 155),
content: ClockWidgetMedium(now: now),
);
await FlutterHomescreenWidget.update(
widgetName: 'ClockWidgetSmall',
size: const Size(155, 155),
content: ClockWidgetSmall(now: now),
);
} catch (e) {
debugPrint('Widget update error: $e');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFF1a1a2e), Color(0xFF16213e), Color(0xFF0f3460)],
),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('Widget Preview',
style: TextStyle(color: Colors.white54, fontSize: 13)),
const SizedBox(height: 24),
ClipRRect(
borderRadius: BorderRadius.circular(20),
child: SizedBox(
width: 329,
height: 155,
child: ClockWidgetMedium(now: _now),
),
),
const SizedBox(height: 16),
ClipRRect(
borderRadius: BorderRadius.circular(20),
child: SizedBox(
width: 155,
height: 155,
child: ClockWidgetSmall(now: _now),
),
),
],
),
),
),
);
}
}
/// Medium clock widget — landscape layout with date and AM/PM indicator.
class ClockWidgetMedium extends StatelessWidget {
final DateTime now;
const ClockWidgetMedium({super.key, required this.now});
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Stack(fit: StackFit.expand, children: [
_Background(),
Positioned(top: -40, right: -20, child: _GlowCircle(160, Colors.purpleAccent.withOpacity(0.25))),
Positioned(bottom: -30, left: 60, child: _GlowCircle(100, Colors.blueAccent.withOpacity(0.2))),
Padding(
padding: const EdgeInsets.all(16),
child: _GlassCard(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_formatTime(now),
style: const TextStyle(
color: Colors.white, fontSize: 52,
fontWeight: FontWeight.w200, letterSpacing: -2, height: 1)),
const SizedBox(height: 6),
Text(_formatDate(now),
style: TextStyle(color: Colors.white.withOpacity(0.55), fontSize: 13)),
],
),
),
Container(
width: 1, height: 60,
color: Colors.white.withOpacity(0.15),
margin: const EdgeInsets.symmetric(horizontal: 20)),
Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Text(_dayOfWeek(now),
style: TextStyle(
color: Colors.white.withOpacity(0.5),
fontSize: 11, letterSpacing: 2, fontWeight: FontWeight.w500)),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.12),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: Text(now.hour < 12 ? 'AM' : 'PM',
style: const TextStyle(
color: Colors.white, fontSize: 12,
fontWeight: FontWeight.w600, letterSpacing: 1)),
),
]),
]),
),
),
),
]));
}
}
/// Small clock widget — compact square layout.
class ClockWidgetSmall extends StatelessWidget {
final DateTime now;
const ClockWidgetSmall({super.key, required this.now});
@override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Stack(fit: StackFit.expand, children: [
_Background(),
Positioned(top: -30, right: -20, child: _GlowCircle(110, Colors.purpleAccent.withOpacity(0.25))),
Positioned(bottom: -20, left: -10, child: _GlowCircle(80, Colors.blueAccent.withOpacity(0.2))),
Padding(
padding: const EdgeInsets.all(12),
child: _GlassCard(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Text(_formatTime(now),
style: const TextStyle(
color: Colors.white, fontSize: 36,
fontWeight: FontWeight.w200, letterSpacing: -1.5, height: 1)),
const SizedBox(height: 6),
Text(_dayOfWeek(now),
style: TextStyle(color: Colors.white.withOpacity(0.45), fontSize: 10, letterSpacing: 2)),
const SizedBox(height: 6),
Text(_formatDateShort(now),
style: TextStyle(color: Colors.white.withOpacity(0.6), fontSize: 12)),
]),
),
),
]),
);
}
}
String _formatTime(DateTime t) {
final h = t.hour % 12 == 0 ? 12 : t.hour % 12;
return '$h:${t.minute.toString().padLeft(2, '0')}';
}
String _formatDate(DateTime t) {
const months = ['January','February','March','April','May','June',
'July','August','September','October','November','December'];
return '${months[t.month - 1]} ${t.day}, ${t.year}';
}
String _formatDateShort(DateTime t) {
const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
return '${months[t.month - 1]} ${t.day}';
}
String _dayOfWeek(DateTime t) {
const days = ['MON','TUE','WED','THU','FRI','SAT','SUN'];
return days[t.weekday - 1];
}
class _Background extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Color(0xFF1a1a2e), Color(0xFF16213e), Color(0xFF0f3460)],
),
),
);
}
}
class _GlowCircle extends StatelessWidget {
final double size;
final Color color;
const _GlowCircle(this.size, this.color);
@override
Widget build(BuildContext context) {
return Container(
width: size, height: size,
decoration: BoxDecoration(
shape: BoxShape.circle, color: color,
boxShadow: [BoxShadow(color: color, blurRadius: 40, spreadRadius: 10)],
),
);
}
}
class _GlassCard extends StatelessWidget {
final Widget child;
const _GlassCard({required this.child});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Colors.white.withOpacity(0.08),
border: Border.all(color: Colors.white.withOpacity(0.18)),
boxShadow: [BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 20, offset: const Offset(0, 4))],
),
child: child,
);
}
}