appwrite_generator 0.1.0 copy "appwrite_generator: ^0.1.0" to clipboard
appwrite_generator: ^0.1.0 copied to clipboard

A powerful Dart CLI tool that generates type-safe, production-ready Dart models from Appwrite configuration JSON files with automatic serialization and null-safety support.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'models/models.dart';
import 'models/projects/project_enums.dart' as project_enums;
import 'models/todos/todo_enums.dart' as todo_enums;

void main() {
  runApp(const TodoApp());
}

class TodoApp extends StatelessWidget {
  const TodoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Appwrite Generator Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const TodoHomePage(),
    );
  }
}

class TodoHomePage extends StatefulWidget {
  const TodoHomePage({super.key});

  @override
  State<TodoHomePage> createState() => _TodoHomePageState();
}

class _TodoHomePageState extends State<TodoHomePage> {
  // Sample data to demonstrate the generated models
  late List<Project> projects;
  late List<Todo> todos;
  late List<Tag> tags;
  late List<Comment> comments;

  int selectedProjectIndex = 0;

  @override
  void initState() {
    super.initState();
    _initializeSampleData();
  }

  void _initializeSampleData() {
    // Create sample projects
    projects = [
      Project(
        title: 'Personal',
        description: 'Personal tasks and errands',
        color: '#2196F3',
        icon: 'person',
        status: project_enums.Status.active,
        createdAt: DateTime.now().subtract(const Duration(days: 30)),
        updatedAt: DateTime.now(),
      ),
      Project(
        title: 'Work',
        description: 'Work-related projects',
        color: '#4CAF50',
        icon: 'work',
        status: project_enums.Status.active,
        createdAt: DateTime.now().subtract(const Duration(days: 20)),
        updatedAt: DateTime.now(),
      ),
      Project(
        title: 'Learning',
        description: 'Educational content and courses',
        color: '#FF9800',
        status: project_enums.Status.active,
        createdAt: DateTime.now().subtract(const Duration(days: 10)),
        updatedAt: DateTime.now(),
      ),
    ];

    // Create sample tags
    tags = [
      Tag(
        name: 'urgent',
        color: '#F44336',
        createdAt: DateTime.now().subtract(const Duration(days: 15)),
      ),
      Tag(
        name: 'bug',
        color: '#E91E63',
        createdAt: DateTime.now().subtract(const Duration(days: 10)),
      ),
      Tag(
        name: 'feature',
        color: '#9C27B0',
        createdAt: DateTime.now().subtract(const Duration(days: 8)),
      ),
    ];

    // Create sample todos
    todos = [
      Todo(
        title: 'Review Appwrite Generator Documentation',
        description: 'Go through the README and understand all features',
        projectId: 'project_1',
        priority: todo_enums.Priority.high,
        status: todo_enums.Status.inProgress,
        dueDate: DateTime.now().add(const Duration(days: 2)),
        estimatedHours: 3,
        createdAt: DateTime.now().subtract(const Duration(days: 2)),
        updatedAt: DateTime.now(),
      ),
      Todo(
        title: 'Set up local Appwrite instance',
        description: 'Install and configure Appwrite for testing',
        projectId: 'project_2',
        priority: todo_enums.Priority.urgent,
        status: todo_enums.Status.todo,
        dueDate: DateTime.now().add(const Duration(days: 1)),
        estimatedHours: 2,
        createdAt: DateTime.now().subtract(const Duration(days: 1)),
        updatedAt: DateTime.now(),
      ),
      Todo(
        title: 'Test generated models',
        description: 'Verify all CRUD operations work with generated code',
        projectId: 'project_2',
        priority: todo_enums.Priority.medium,
        status: todo_enums.Status.todo,
        estimatedHours: 5,
        createdAt: DateTime.now().subtract(const Duration(hours: 12)),
        updatedAt: DateTime.now(),
      ),
      Todo(
        title: 'Learn Flutter state management',
        description: 'Study Provider, Riverpod, and Bloc patterns',
        projectId: 'project_3',
        priority: todo_enums.Priority.low,
        status: todo_enums.Status.todo,
        dueDate: DateTime.now().add(const Duration(days: 7)),
        estimatedHours: 10,
        createdAt: DateTime.now().subtract(const Duration(hours: 6)),
        updatedAt: DateTime.now(),
      ),
      Todo(
        title: 'Buy groceries',
        description: 'Milk, eggs, bread, vegetables',
        projectId: 'project_1',
        priority: todo_enums.Priority.medium,
        status: todo_enums.Status.done,
        completedAt: DateTime.now().subtract(const Duration(hours: 2)),
        estimatedHours: 1,
        createdAt: DateTime.now().subtract(const Duration(days: 1)),
        updatedAt: DateTime.now(),
      ),
    ];

    // Create sample comments
    comments = [
      Comment(
        todoId: 'todo_1',
        content: 'The generator documentation is very comprehensive!',
        authorName: 'John Doe',
        createdAt: DateTime.now().subtract(const Duration(hours: 5)),
        updatedAt: DateTime.now().subtract(const Duration(hours: 5)),
      ),
      Comment(
        todoId: 'todo_1',
        content: 'Make sure to check out the enum generation feature',
        authorName: 'Jane Smith',
        createdAt: DateTime.now().subtract(const Duration(hours: 3)),
        updatedAt: DateTime.now().subtract(const Duration(hours: 3)),
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    final currentProject = projects[selectedProjectIndex];
    final projectTodos = todos.where((t) =>
      t.projectId == 'project_${selectedProjectIndex + 1}'
    ).toList();

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Appwrite Generator - To-Do Example'),
      ),
      drawer: _buildProjectDrawer(),
      body: Column(
        children: [
          _buildProjectHeader(currentProject),
          _buildStatsCard(projectTodos),
          const Padding(
            padding: EdgeInsets.all(16.0),
            child: Text(
              'Tasks',
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
          ),
          Expanded(
            child: projectTodos.isEmpty
                ? const Center(child: Text('No tasks in this project'))
                : ListView.builder(
                    itemCount: projectTodos.length,
                    itemBuilder: (context, index) {
                      return _buildTodoCard(projectTodos[index]);
                    },
                  ),
          ),
        ],
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          _showAddTodoDialog(context);
        },
        icon: const Icon(Icons.add),
        label: const Text('Add Task'),
      ),
    );
  }

  Widget _buildProjectDrawer() {
    return Drawer(
      child: ListView(
        padding: EdgeInsets.zero,
        children: [
          const DrawerHeader(
            decoration: BoxDecoration(
              gradient: LinearGradient(
                colors: [Colors.blue, Colors.blueAccent],
              ),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Icon(Icons.dashboard, size: 48, color: Colors.white),
                SizedBox(height: 8),
                Text(
                  'Projects',
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 24,
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ],
            ),
          ),
          ...projects.asMap().entries.map((entry) {
            final index = entry.key;
            final project = entry.value;
            return ListTile(
              leading: Icon(
                _getIconData(project.icon),
                color: _parseColor(project.color),
              ),
              title: Text(project.title),
              subtitle: Text(project.description ?? ''),
              selected: selectedProjectIndex == index,
              onTap: () {
                setState(() {
                  selectedProjectIndex = index;
                });
                Navigator.pop(context);
              },
            );
          }),
          const Divider(),
          ListTile(
            leading: const Icon(Icons.label),
            title: const Text('All Tags'),
            subtitle: Text('${tags.length} tags'),
            onTap: () {
              Navigator.pop(context);
              _showTagsDialog(context);
            },
          ),
        ],
      ),
    );
  }

  Widget _buildProjectHeader(Project project) {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: _parseColor(project.color).withOpacity(0.1),
        border: Border(
          bottom: BorderSide(
            color: _parseColor(project.color),
            width: 2,
          ),
        ),
      ),
      child: Row(
        children: [
          Icon(
            _getIconData(project.icon),
            size: 32,
            color: _parseColor(project.color),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  project.title,
                  style: const TextStyle(
                    fontSize: 24,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                if (project.description != null)
                  Text(
                    project.description!,
                    style: TextStyle(
                      fontSize: 14,
                      color: Colors.grey[600],
                    ),
                  ),
              ],
            ),
          ),
          _buildStatusChip(project.status),
        ],
      ),
    );
  }

  Widget _buildStatsCard(List<Todo> projectTodos) {
    final total = projectTodos.length;
    final completed = projectTodos.where((t) => t.status == todo_enums.Status.done).length;
    final inProgress = projectTodos.where((t) => t.status == todo_enums.Status.inProgress).length;
    final blocked = projectTodos.where((t) => t.status == todo_enums.Status.blocked).length;

    return Card(
      margin: const EdgeInsets.all(16),
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceAround,
          children: [
            _buildStatItem('Total', total, Colors.blue),
            _buildStatItem('Done', completed, Colors.green),
            _buildStatItem('In Progress', inProgress, Colors.orange),
            if (blocked > 0) _buildStatItem('Blocked', blocked, Colors.red),
          ],
        ),
      ),
    );
  }

  Widget _buildStatItem(String label, int count, Color color) {
    return Column(
      children: [
        Text(
          count.toString(),
          style: TextStyle(
            fontSize: 24,
            fontWeight: FontWeight.bold,
            color: color,
          ),
        ),
        Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: Colors.grey[600],
          ),
        ),
      ],
    );
  }

  Widget _buildTodoCard(Todo todo) {
    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: InkWell(
        onTap: () => _showTodoDetails(context, todo),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  Expanded(
                    child: Text(
                      todo.title,
                      style: TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                        decoration: todo.status == todo_enums.Status.done
                            ? TextDecoration.lineThrough
                            : null,
                      ),
                    ),
                  ),
                  _buildPriorityBadge(todo.priority),
                ],
              ),
              if (todo.description != null) ...[
                const SizedBox(height: 8),
                Text(
                  todo.description!,
                  style: TextStyle(
                    color: Colors.grey[600],
                    fontSize: 14,
                  ),
                  maxLines: 2,
                  overflow: TextOverflow.ellipsis,
                ),
              ],
              const SizedBox(height: 12),
              Row(
                children: [
                  _buildStatusChip(todo.status),
                  const SizedBox(width: 8),
                  if (todo.dueDate != null) ...[
                    Icon(Icons.calendar_today, size: 16, color: Colors.grey[600]),
                    const SizedBox(width: 4),
                    Text(
                      DateFormat('MMM dd').format(todo.dueDate!),
                      style: TextStyle(
                        fontSize: 12,
                        color: _isOverdue(todo) ? Colors.red : Colors.grey[600],
                        fontWeight: _isOverdue(todo) ? FontWeight.bold : FontWeight.normal,
                      ),
                    ),
                    const SizedBox(width: 8),
                  ],
                  if (todo.estimatedHours != null) ...[
                    Icon(Icons.access_time, size: 16, color: Colors.grey[600]),
                    const SizedBox(width: 4),
                    Text(
                      '${todo.estimatedHours}h',
                      style: TextStyle(fontSize: 12, color: Colors.grey[600]),
                    ),
                  ],
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildPriorityBadge(todo_enums.Priority priority) {
    Color color;
    IconData icon;

    switch (priority) {
      case todo_enums.Priority.urgent:
        color = Colors.red;
        icon = Icons.priority_high;
      case todo_enums.Priority.high:
        color = Colors.orange;
        icon = Icons.arrow_upward;
      case todo_enums.Priority.medium:
        color = Colors.blue;
        icon = Icons.remove;
      case todo_enums.Priority.low:
        color = Colors.grey;
        icon = Icons.arrow_downward;
    }

    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      decoration: BoxDecoration(
        color: color.withOpacity(0.1),
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: color),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(icon, size: 14, color: color),
          const SizedBox(width: 4),
          Text(
            priority.value.toUpperCase(),
            style: TextStyle(
              fontSize: 10,
              fontWeight: FontWeight.bold,
              color: color,
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildStatusChip(dynamic status) {
    Color color;
    String label;

    if (status is project_enums.Status) {
      switch (status) {
        case project_enums.Status.active:
          color = Colors.green;
          label = 'Active';
        case project_enums.Status.archived:
          color = Colors.grey;
          label = 'Archived';
        case project_enums.Status.completed:
          color = Colors.blue;
          label = 'Completed';
      }
    } else {
      // Status enum
      switch (status as todo_enums.Status) {
        case todo_enums.Status.todo:
          color = Colors.grey;
          label = 'To Do';
        case todo_enums.Status.inProgress:
          color = Colors.orange;
          label = 'In Progress';
        case todo_enums.Status.blocked:
          color = Colors.red;
          label = 'Blocked';
        case todo_enums.Status.done:
          color = Colors.green;
          label = 'Done';
      }
    }

    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      decoration: BoxDecoration(
        color: color.withOpacity(0.1),
        borderRadius: BorderRadius.circular(12),
      ),
      child: Text(
        label,
        style: TextStyle(
          fontSize: 12,
          fontWeight: FontWeight.bold,
          color: color,
        ),
      ),
    );
  }

  bool _isOverdue(Todo todo) {
    if (todo.dueDate == null || todo.status == todo_enums.Status.done) return false;
    return todo.dueDate!.isBefore(DateTime.now());
  }

  Color _parseColor(String? hexColor) {
    if (hexColor == null) return Colors.blue;
    final hex = hexColor.replaceAll('#', '');
    return Color(int.parse('FF$hex', radix: 16));
  }

  IconData _getIconData(String? iconName) {
    switch (iconName) {
      case 'person':
        return Icons.person;
      case 'work':
        return Icons.work;
      default:
        return Icons.folder;
    }
  }

  void _showTodoDetails(BuildContext context, Todo todo) {
    final relatedComments = comments.where((c) => c.todoId == 'todo_1').toList();

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text(todo.title),
        content: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              if (todo.description != null) ...[
                const Text(
                  'Description',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                const SizedBox(height: 4),
                Text(todo.description!),
                const SizedBox(height: 16),
              ],
              _buildDetailRow('Priority', todo.priority.value),
              _buildDetailRow('Status', todo.status.value),
              if (todo.dueDate != null)
                _buildDetailRow(
                  'Due Date',
                  DateFormat('MMM dd, yyyy').format(todo.dueDate!),
                ),
              if (todo.estimatedHours != null)
                _buildDetailRow('Estimated', '${todo.estimatedHours} hours'),
              if (todo.completedAt != null)
                _buildDetailRow(
                  'Completed',
                  DateFormat('MMM dd, yyyy HH:mm').format(todo.completedAt!),
                ),
              const SizedBox(height: 16),
              const Text(
                'Comments',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
              ),
              const SizedBox(height: 8),
              if (relatedComments.isEmpty)
                const Text('No comments yet')
              else
                ...relatedComments.map((comment) => Card(
                      margin: const EdgeInsets.only(bottom: 8),
                      child: Padding(
                        padding: const EdgeInsets.all(12),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              comment.authorName ?? 'Anonymous',
                              style: const TextStyle(fontWeight: FontWeight.bold),
                            ),
                            const SizedBox(height: 4),
                            Text(comment.content),
                            const SizedBox(height: 4),
                            Text(
                              DateFormat('MMM dd, HH:mm').format(comment.createdAt),
                              style: TextStyle(
                                fontSize: 12,
                                color: Colors.grey[600],
                              ),
                            ),
                          ],
                        ),
                      ),
                    )),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  Widget _buildDetailRow(String label, String value) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 8),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 100,
            child: Text(
              '$label:',
              style: const TextStyle(fontWeight: FontWeight.bold),
            ),
          ),
          Expanded(child: Text(value)),
        ],
      ),
    );
  }

  void _showAddTodoDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Add New Task'),
        content: const Text(
          'This is a demo app showcasing the Appwrite Generator.\n\n'
          'In a real app, this would show a form to create a new task '
          'using the generated Todo model with proper validation and '
          'Appwrite SDK integration.',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Got it'),
          ),
        ],
      ),
    );
  }

  void _showTagsDialog(BuildContext context) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('All Tags'),
        content: SizedBox(
          width: double.maxFinite,
          child: ListView.builder(
            shrinkWrap: true,
            itemCount: tags.length,
            itemBuilder: (context, index) {
              final tag = tags[index];
              return ListTile(
                leading: Container(
                  width: 24,
                  height: 24,
                  decoration: BoxDecoration(
                    color: _parseColor(tag.color),
                    shape: BoxShape.circle,
                  ),
                ),
                title: Text(tag.name),
                subtitle: Text(
                  'Created ${DateFormat('MMM dd, yyyy').format(tag.createdAt)}',
                ),
              );
            },
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }
}
0
likes
150
points
102
downloads

Publisher

unverified uploader

Weekly Downloads

A powerful Dart CLI tool that generates type-safe, production-ready Dart models from Appwrite configuration JSON files with automatic serialization and null-safety support.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

args, cli_completion, mason_logger, mustache_template, path, pub_updater, recase, yaml

More

Packages that depend on appwrite_generator