off_ide 0.1.4 copy "off_ide: ^0.1.4" to clipboard
off_ide: ^0.1.4 copied to clipboard

A high-performance, VS Code-like workspace shell widget for Flutter applications, featuring split editors, tab management, and a customizable sidebar.

Off IDE #

A high-performance, VS Code-like workspace shell widget for Flutter applications.

Live Demo

Off IDE Demo

Features #

Feature Description
VS Code Layout Familiar structure with Activity Bar, Sidebar, and Editor Area
Split Editor Vertical split panes to view multiple tabs side-by-side
Tab Management Close actions, dirty state indicators, and drag-and-drop reordering
Unsaved Changes Guard onBeforeCloseTab callback to prompt before closing dirty tabs
Keyboard Shortcuts Browser-safe defaults (Alt+W, Alt+], Alt+[) with consumer overrides
Text Selection SelectionArea wrapping for copy-paste in browser deployments
Persistence Auto-restore open tabs and sidebar state via HydratedBloc
Web Ready Responsive design, browser-safe shortcuts, and persistence support
High Performance O(1) state lookups and granular rebuilds for large widget trees
Custom Icons Dynamic iconWidget support (SVGs, images) with tree-shaking preserved
Customizable Configurable activity bar, sidebar views, page registry, and shortcuts
Theme Aware Integrates with ThemeData, supporting light and dark modes

Getting Started #

Add off_ide to your pubspec.yaml:

dependencies:
  off_ide: ^0.1.4

Usage #

The main entry point is the WorkspaceShell widget, which requires a WorkspaceConfig.

There are two primary ways to define this configuration: Hardcoded (for simple, static apps) and Dynamic (for robust, backend-driven apps like CRMs).

1. Basic Usage (Hardcoded) #

For simple applications where the sidebar structure never changes, you can define your WorkspaceConfig statically:

import 'package:flutter/material.dart';
import 'package:off_ide/off_ide.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: WorkspaceShell(
        config: WorkspaceConfig(
          // 1. Define Activity Bar Items
          activityBarItems: [
            const ActivityBarItem(
              id: 'files',
              icon: Icons.folder_outlined,
              label: 'Explorer',
            ),
          ],
          // 2. Define Sidebar Content
          sidebarViews: {
            'files': const SidebarView(
              id: 'files',
              title: 'EXPLORER',
              groups: [
                 MenuGroup(
                   id: 'project',
                   label: 'My Project',
                   items: [
                     MenuItem(
                       id: 'readme',
                       label: 'README.md',
                       pageId: 'markdown_viewer',
                       icon: Icons.description_outlined,
                     ),
                   ],
                 ),
              ],
            ),
          },
          // 3. Register Page Builders
          pageRegistry: {
            'markdown_viewer': (context, args) => const Center(child: Text('README')),
          },
        ),
      ),
    );
  }
}

2. Unsaved Changes Guard #

Protect users from losing unsaved work by providing an onBeforeCloseTab callback:

WorkspaceConfig(
  onBeforeCloseTab: (context, tab) async {
    return await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        title: const Text('Unsaved Changes'),
        content: Text('"${tab.title}" has unsaved changes. Discard?'),
        actions: [
          TextButton(onPressed: () => Navigator.pop(ctx, false), child: const Text('Cancel')),
          FilledButton(onPressed: () => Navigator.pop(ctx, true), child: const Text('Discard')),
        ],
      ),
    ) ?? false;
  },
  // ... other config
)

Mark tabs dirty from your forms via the BLoC:

context.read<WorkspaceBloc>().add(MarkTabDirty(tabId: tabId, isDirty: true));

3. Keyboard Shortcuts #

Built-in browser-safe shortcuts (these don't conflict with browser hotkeys):

Shortcut Action
Alt + W Close active tab
Alt + ] Cycle to next tab
Alt + [ Cycle to previous tab

Add custom shortcuts via config:

WorkspaceConfig(
  keyboardShortcuts: {
    const SingleActivator(LogicalKeyboardKey.keyP, control: true): 
      () => showCommandPalette(context),
  },
  // ... other config
)

The WorkspaceConfig is built to be extremely flexible. For complex applications, it is highly recommended to dynamically generate your sidebar using backend-driven JSON schemas combined with a state manager (like flutter_bloc).

This approach easily enables Role-Based Access Control (RBAC).

Implementation Example

By wrapping WorkspaceShell in a state listener, the shell will seamlessly update and automatically close restricted tabs when roles change!

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Listen to your Auth/Role State Manager
    return BlocBuilder<RoleCubit, String>(
      builder: (context, currentRole) {
        return MaterialApp(
          home: WorkspaceShell(
            config: WorkspaceConfig(
              
              // 1. Parse your backend JSON and filter by `currentRole`
              activityBarItems: CustomParser.parseActivityBar(jsonSchema, currentRole),
              sidebarViews: CustomParser.parseSidebars(jsonSchema, currentRole),
              
              // 2. Only provide page builders the user has access to
              pageRegistry: CustomParser.getPageRegistry(currentRole),
            ),
          ),
        );
      },
    );
  }
}

Key Dynamic Features:

  1. Discard Restricted Items: Overwrite your CustomParser to check if a user has permission to see a JSON item. If not, don't instantiate the MenuItem.
  2. Auto-Purge Tabs: Because WorkspaceShell is deeply stateful, if the user's role changes, the shell will automatically purge any open tabs that no longer exist in the updated pageRegistry.
  3. Custom Backend Icons: Make use of the iconWidget parameter to parse custom backend icons (SVGs, Image Assets, custom Flutter code) instead of standard IconData to ensure Flutter web compilation works smoothly with tree-shaking.

See the example/lib/main.dart source code to view a complete, production-ready implementation of dynamic JSON parsing and an interactive RBAC role toggle.

Additional Resources #

Check out the example directory for a complete, runnable sample application.

License #

MIT

2
likes
160
points
311
downloads

Publisher

unverified uploader

Weekly Downloads

A high-performance, VS Code-like workspace shell widget for Flutter applications, featuring split editors, tab management, and a customizable sidebar.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

equatable, flutter, flutter_bloc, hydrated_bloc, path_provider, uuid

More

Packages that depend on off_ide