utopia_cms 0.2.0 copy "utopia_cms: ^0.2.0" to clipboard
utopia_cms: ^0.2.0 copied to clipboard

Utopia CMS - core

Utopia CMS (Core) #

The Utopia CMS Core package is a data visualization library written in Flutter for creating beautiful, animated, high-performance and flexible CMS panels, which are used to manage databases for mobile apps.

Motivation #

Creating CMS panels in Flutter can be costly compared to using No-Code/Low-Code solutions. However, we believe that it is still beneficial for a project as it ensures maintainability and allows for the creation of outstanding UI, which is often lacking in existing solutions. That's why we have developed this Low-Code library to optimize the process of creating customizable panels.

Example #

This is a simple example which integrates with GraphQL server and creates complete CMS layout with one page and management flow.

class Example extends StatelessWidget {
  final String? pageId;
  final void Function(String pageId) onPageChanged;
  final GraphQLClient client;

  const Example({required this.pageId, required this.onPageChanged, required this.client});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: CmsWidget(
        selectedPageId: MutableValue.delegate(() => pageId ?? 'users', onPageChanged),
        items: [
          CmsWidgetItem.page(
            id: 'products',
            icon: Icon(Icons.shopping_basket_outlined),
            title: Text('Products'),
            content: _buildProductsPage(),
          ),
        ],
      ),
    );
  }

  CmsTablePage _buildProductsPage() {
    return CmsTablePage(
      title: "Products",
      delegate: CmsHasura.delegate(
        client: client,
        table: Tables.products,
        fields: TableFields.products,
        archivedFilter: const CmsFilterNotEquals("archived", true),
      ),
      entries: [
        CmsTextEntry(key: "name", modifier: const CmsEntryModifier(sortable: true)),
        CmsTextEntry(key: "description", flex: 4),
      ],
    );
  }
}

CMS overview & basic features #

The utopia_cms_core library provides the following features for creating server-layer, responsive table-based pages, edit/create flows, and internal navigation. It also supports integration with custom pages and offers a set of helpful widgets to maintain a coherent theme in your application.

CmsWidget #

Wraps whole application and creates a proper paging behavior with usage of CmsMenu.

CmsThemeData #

Modifies the styling of the Widgets, determines fonts and colors.

CmsTable #

This is a standalone widget for table-based content management. By default, it displays a sortable and filterable table, introduces 25 item infinite-scroll paging, creates edit and create subpages, and supports item removal. The data is provided by CmsDelegate and displayed by CmsEntry. Requests may be filtered via CmsFilterEntry.

CmsEntry #

This interface handles the display and management of data. There is a pre-created set of primitives for interacting with basic data types.

Name Description
CmsTextEntry Handles generic String variables
CmsNumEntry Handles numeric variables
CmsDropdownEntry For managing set of options and singular choice
CmsBoolEntry Handles bool variables
CmsDateEntry Handles Date variables
CmsMediaEntry Handles files (img, vid, doc, unknown)
CmsToManyDropdownEntry M2M relationships multi selection dropdown

You can create custom entries by referring to the implementation of any primitive and the CmsEntry

CmsFilterEntry #

This interface handles filtering fields of the CmsTable.

Name Description
CmsFilterSearchEntry Handles generic String full search
CmsFilterDateEntry Handles date ranges

You can create custom entries by referring to the implementation of any primitive and the CmsFilterEntry

CmsDelegate #

This is the main interface for handling CmsTable. It is not suitable on its own for handling to-many relationships. To achieve this functionality, refer to Relationships

The library provides pre-created delegates:

Name Description
utopia_cms_firebase Firebase delegate integration
utopia_cms_graphql Generic GraphQL delegate integration
utopia_cms_hasura Hasura delegate integration

To create your custom delegate, refer to the implementation of any delegate and the CmsDelegate interface.

Relationships #

Handling relation-based entries in the system is slightly more complex. For a particular entry, you need to use the CmsToManyDelegate and register additional callbacks to CmsItemManagementBaseState that is available via Provider

final baseState = Provider.of<CmsItemManagementBaseState>(context);

baseState.addOnSavedCallback(
    (value) async {
        return delegate.update(...);
    }
);

The library provides the following existing solutions for relationships:

Existing CmsToManyDelegate implementations Source
CmsHasuraOneToManyDelegate, CmsHasuraManyToManyDelegate utopia_cms_hasura

Media #

Media such as images or videos are handled by CmsMediaDelegate which introduces upload and delete functions.

The library provides no existing generic solutions for relationships yet, but here's an example

class FileDelegate implements CmsMediaDelegate {
  final CmsGraphQLService graphQLService;
  final GraphQLClient client;

  const FileDelegate(this.graphQLService, this.client);

  Future<({String downloadUrl, CmsFileRef ref})> upload(XFile file) async {
    final (uploadUrl, downloadUrl) = await _createAttachment(mimeType: file.mimeType!);
    await _upload(uploadUrl, file);
    return (downloadUrl: downloadUrl, ref: "XD");
  }

  Future<(String, String)> _createAttachment({required String mimeType}) async {
    final result = await graphQLService.mutate(
      client,
      name: 'createAttachment',
      arguments: {'data': {'contentType': mimeType}.toValueNodeUnsafe()},
      fields: {CmsGraphQLField('uploadUrl'), CmsGraphQLField('downloadUrl')},
    );
    result as Map<String, dynamic>;
    return (result['uploadUrl'] as String, result['downloadUrl'] as String);
  }

  Future<void> _upload(String url, XFile file) async {
    final webFile = await HttpRequest.request(file.path, responseType: 'blob');
    final request = await HttpRequest.request(url, method: 'PUT', mimeType: file.mimeType!, sendData: webFile.response);
    if(request.status != HttpStatus.ok) throw Exception("Failed to upload");
  }
}

Widgets #

The package exports its basic UI components in order to allow maintaining a coherent theme in your custom pages.

  • CmsFieldWrapper
  • CmsTextField
  • CmsButton
  • CmsSwitch
  • CmsLoader
  • CmsMockLoadingBox
  • CmsHeader

Contributions #

Contributions are welcomed!

If you want to support our project, feel free to open a pull-request.