Make a OKRs Tracker App with Flutter
What This App Does
A OKRs Tracker app serves a specific need in today's mobile-first world. Building one with Flutter means you ship to iOS, Android, and the web from a single codebase, cutting development time dramatically while keeping a native-quality experience.
Main Features
| Feature | Why It Matters |
|---|---|
| OKR Hierarchy | Company → Department → Individual: align objectives across levels |
| Key Result Scoring | Numeric progress (0–100%) or boolean KRs with manual/auto updates |
| Check-In Reminders | Weekly prompt to update progress before team review |
| Confidence Ratings | Traffic-light indicator (Red/Yellow/Green) for each objective's outlook |
How to Make a OKRs Tracker App with Flutter
OKR Hierarchy
class Objective {
final String id, title;
double progress; // 0–100
final String level; // company, department, individual
final List keyResults;
final List children;
Objective({required this.id, required this.title, this.progress = 0,
required this.level, this.keyResults = const [], this.children = const []});
}
class KeyResult {
String title;
double current, target;
bool isBoolean;
KeyResult({required this.title, this.current = 0, this.target = 1, this.isBoolean = false});
double get progressPercent => isBoolean ? (current >= target ? 100 : 0) : (current / target * 100).clamp(0, 100);
}
class OkrTreeWidget extends StatelessWidget {
final Objective objective;
const OkrTreeWidget({super.key, required this.objective});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(8),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
_confidenceDot(objective.progress),
const SizedBox(width: 8),
Text(objective.title, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
const Spacer(),
Text('\${objective.progress.toStringAsFixed(0)}%', style: TextStyle(
fontWeight: FontWeight.bold, color: _progressColor(objective.progress))),
]),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: objective.progress / 100,
minHeight: 8,
backgroundColor: Colors.grey.shade200,
),
),
if (objective.keyResults.isNotEmpty) ...[
const SizedBox(height: 12),
...objective.keyResults.map((kr) => Padding(
padding: const EdgeInsets.only(left: 16, top: 4),
child: Row(children: [
Text('• \${kr.title}', style: TextStyle(color: Colors.grey.shade600)),
const Spacer(),
Text('\${kr.current.toStringAsFixed(1)} / \${kr.target.toStringAsFixed(1)}'),
]),
)),
],
if (objective.children.isNotEmpty)
...objective.children.map((child) => OkrTreeWidget(objective: child)),
],
),
),
);
}
Widget _confidenceDot(double progress) {
final color = progress < 40 ? Colors.red : (progress < 70 ? Colors.orange : Colors.green);
return Container(width: 12, height: 12, decoration: BoxDecoration(shape: BoxShape.circle, color: color));
}
Color _progressColor(double p) => p < 40 ? Colors.red : (p < 70 ? Colors.orange : Colors.green);
}
Recommended Libraries
| Package | Purpose |
|---|---|
| fl_chart | Progress bars and radar charts |
| flutter_local_notifications | Weekly check-in reminders |
UI & UX Suggestions
Use a white cards, red/yellow/green confidence dots, clean sans-serif colour palette to match the app's purpose. Keep the navigation simple — a bottom nav bar or a single-scroll layout works best for this type of app. Make sure every interaction provides haptic or visual feedback so the app feels responsive and polished.