flutter_android_widgets 0.0.2
flutter_android_widgets: ^0.0.2 copied to clipboard
Define Android home screen widgets entirely in Dart. No XML, no Kotlin, no manual manifest editing.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_android_widgets/flutter_android_widgets.dart';
import 'my_widgets.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
WidgetUpdater.initialize(widgets: [myWidget]);
HomeWidgetData.autoUpdate = true;
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Widget Demo',
theme: ThemeData(
colorSchemeSeed: const Color(0xFF1A1A2E),
useMaterial3: true,
),
home: const WidgetDemoPage(),
);
}
}
class WidgetDemoPage extends StatefulWidget {
const WidgetDemoPage({super.key});
@override
State<WidgetDemoPage> createState() => _WidgetDemoPageState();
}
class _WidgetDemoPageState extends State<WidgetDemoPage> {
final _nameController = TextEditingController(text: 'John');
String _status = '';
bool _saving = false;
@override
void dispose() {
_nameController.dispose();
super.dispose();
}
Future<void> _updateWidget() async {
final name = _nameController.text.trim();
if (name.isEmpty) return;
setState(() => _saving = true);
try {
final now = TimeOfDay.now();
final hour = now.hour.toString().padLeft(2, '0');
final minute = now.minute.toString().padLeft(2, '0');
await HomeWidgetData.saveAll({
'userName': name,
'lastUpdated': 'Updated at $hour:$minute',
});
setState(
() => _status = '✓ Widget updated! Changes applied to home screen.',
);
} catch (e) {
setState(() => _status = 'Error: $e');
} finally {
setState(() => _saving = false);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
appBar: AppBar(
title: const Text('flutter_android_widgets'),
centerTitle: true,
backgroundColor: const Color(0xFF1A1A2E),
foregroundColor: Colors.white,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Widget preview card
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFF1A1A2E),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withAlpha(40),
blurRadius: 12,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Widget Preview',
style: TextStyle(color: Colors.white54, fontSize: 11),
),
const SizedBox(height: 8),
Text(
_nameController.text.isEmpty
? 'Widget User'
: _nameController.text,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
_status.startsWith('✓')
? _status.replaceFirst(
'✓ Widget updated! Changes applied to home screen.',
'Updated — widget refreshed automatically',
)
: 'Tap "Send to Widget" below',
style: const TextStyle(
color: Color(0xFFAAAAAA),
fontSize: 13,
),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 14,
vertical: 7,
),
decoration: BoxDecoration(
color: Colors.white24,
borderRadius: BorderRadius.circular(6),
),
child: const Text(
'Refresh',
style: TextStyle(color: Colors.white, fontSize: 13),
),
),
],
),
),
const SizedBox(height: 28),
// Controls
Card(
elevation: 0,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text(
'Update widget data',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
const SizedBox(height: 12),
TextField(
controller: _nameController,
onChanged: (_) => setState(() {}),
decoration: const InputDecoration(
labelText: 'Display name',
hintText: 'e.g. John',
border: OutlineInputBorder(),
isDense: true,
),
),
const SizedBox(height: 12),
FilledButton(
onPressed: _saving ? null : _updateWidget,
child: _saving
? const SizedBox(
height: 18,
width: 18,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text('Send to Widget'),
),
if (_status.isNotEmpty) ...[
const SizedBox(height: 10),
Text(
_status,
style: TextStyle(
fontSize: 13,
color: _status.startsWith('✓')
? Colors.green.shade700
: Colors.red.shade700,
),
),
],
],
),
),
),
const SizedBox(height: 20),
// How-to steps
Card(
elevation: 0,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
child: const Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'How to test',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 15,
),
),
SizedBox(height: 10),
_Step(
n: 1,
text: 'Long-press your home screen and choose Widgets',
),
_Step(
n: 2,
text: 'Find "Widget Demo" and add it to the home screen',
),
_Step(
n: 3,
text:
'Come back here, type a name, and tap "Send to Widget"',
),
_Step(
n: 4,
text:
'Tap the Refresh button on the widget — it updates instantly',
),
],
),
),
),
],
),
),
);
}
}
class _Step extends StatelessWidget {
final int n;
final String text;
const _Step({required this.n, required this.text});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 22,
height: 22,
alignment: Alignment.center,
decoration: const BoxDecoration(
color: Color(0xFF1A1A2E),
shape: BoxShape.circle,
),
child: Text(
'$n',
style: const TextStyle(
color: Colors.white,
fontSize: 11,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(width: 10),
Expanded(child: Text(text, style: const TextStyle(fontSize: 13))),
],
),
);
}
}