mydomain
No ADS
No ADS

FlutterArtist Internal Shelf Event ex1

  1. Structure of the example
  2. SalesOrder96aShelf
  3. Create SalesOrderLine
  4. Edit SalesOrderLine
  5. Create SalesOrder
In FlutterArtist, Block(s), Scalar(s), and the Shelf are designed to function as a unified entity. They will respond shortly thereafter (re-query data or refresh ITEM) whenever an internal event is triggered from any component within the same Shelf.
Situations that trigger internal Shelf events:
  • Creating or modifying an ITEM via a Form.
  • Deleting one or more ITEM(s) on a Block.
  • Executing Block QuickAction(s), such as BlockQuickItemCreationAction, BlockQuickItemUpdateAction, or BlockQuickMultiItemCreationAction.
  • Executing BlockBackendAction(s).
  • Executing ScalarBackendAction(s).
Scenario: Demo96a
In this article, we will explore how to implement a Shelf with two Block(s) in a parent-child relationship and two FormModel(s) linked to them through a classic case - SalesOrder and SalesOrderLine(s).
The main screen of the example is divided into two parts:
  • The left-hand area displays a list of recent orders (SalesOrder).
  • The right-hand area displays the current order information and its details (SalesOrderLine(s)) in a table.
Users can add/modify order information or detail lines through separate Dialog Form(s).
Whenever a user creates, modifies, or deletes a SalesOrderLine, an internal Shelf event is emitted and captured by the parent Block (SalesOrder96aBlock), triggering this Block to refresh the current ITEM. This ensures that the user sees the "Total Amount" and "Total Tax Amount" updated on the interface shortly after the changes at the detail level are successfully executed.
Note: This example is quite complex; its full source code is available in the FlutterArtist Demo below.
  • Download FlutterArtist Demo

1. Structure of the example

On the main screen, the left side displays a list of SalesOrder(s), and the right side shows the current SalesOrder and a list of SalesOrderLine(s).
When the user clicks the "Create New SalesOrderLine" button, a dialog box will appear allowing you to enter information into a Form.

2. SalesOrder96aShelf

First, let's examine the Shelf definition, followed by a detailed analysis.
SalesOrder96aShelf
class SalesOrder96aShelf extends Shelf {
  @override
  ShelfStructure defineShelfStructure() {
    return ShelfStructure(
      description: null,
      filterModels: {},
      blocks: [
        SalesOrder96aBlock(
          name: SalesOrder96aBlock.blkName,
          description: null,
          config: BlockConfig(
            pageable: Pageable(pageSize: 3),
            onInternalShelfEvents: InternalShelfEventBlockRecipient(
              itemLevelReactionOn: [
                Evt.ofBlock(SalesOrderLine96aBlock.blkName),
              ],
            ),
          ),
          filterModelName: null,
          formModel: SalesOrder96aFormModel(),
          childBlocks: [
            SalesOrderLine96aBlock(
              name: SalesOrderLine96aBlock.blkName,
              description: null,
              config: BlockConfig(),
              filterModelName: null,
              formModel: SalesOrderLine96aFormModel(),
              childBlocks: [],
            ),
          ],
        ),
      ],
    );
  }

  SalesOrder96aBlock findSalesOrder96aBlock() {
    return findBlock(SalesOrder96aBlock.blkName) as SalesOrder96aBlock;
  }

  SalesOrderLine96aBlock findSalesOrderLine96aBlock() {
    return findBlock(SalesOrderLine96aBlock.blkName) as SalesOrderLine96aBlock;
  }
}
SalesOrderLine96aBlock Config
The configuration of SalesOrderLine96aBlock indicates it includes a FormModel, meaning it supports creating or modifying its ITEM(s) via a Form. This is the event source triggered whenever a user creates, modifies, and saves data successfully.
SalesOrderLine96aBlock Config
SalesOrderLine96aBlock(
  name: SalesOrderLine96aBlock.blkName,
  description: null,
  config: BlockConfig(),
  filterModelName: null,
  formModel: SalesOrderLine96aFormModel(),
  childBlocks: [],
),
No ADS
SalesOrder96aBlock Config
The SalesOrder96aBlock configuration specifies that it will react to internal Shelf events:
  • itemLevelReactionOn: Defines the event sources that trigger this Block to react at the ITEM level. In other words, the current ITEM of this Block (Sales Order) will be automatically refreshed.
SalesOrder96aBlock Config
SalesOrder96aBlock(
  name: SalesOrder96aBlock.blkName,
  description: null,
  config: BlockConfig(
    pageable: Pageable(pageSize: 3),
    onInternalShelfEvents: InternalShelfEventBlockRecipient(
      itemLevelReactionOn: [
        Evt.ofBlock(SalesOrderLine96aBlock.blkName),
      ],
    ),
  ),
  ...
),
The illustration below describes the difference between two operational flows: when SalesOrder96aBlock does not listen versus when it listens to internal Shelf events emitted from SalesOrderLine96aBlock.
  • Without Listening: After modifying an order line, the total amount on the parent order remains unchanged until the user manually refreshes it.
  • With Listening: Immediately after the TaskUnit for updating the order line is completed, a TaskUnit to refresh the parent order is queued and executed, ensuring the interface always displays the latest figures.

3. Create SalesOrderLine

When the user clicks the create button on the BlockControlBar of SalesOrderLine96aBlock, the onNavigateCreate() callback is triggered. Here, you must implement the logic to open the SalesOrderLine96aDialog.
BlockControlBar (salesOrderLineBlock)
BlockControlBar(
  description: null,
  ownerClassInstance: this,
  block: salesOrderLineBlock,
  config: BlockControlBarConfig( 
    allowCreateButton: true, 
    onNavigateCreate: (PrepareItemCreationResult result) {
      _onNavigateCreateSalesOrderLine(context, result);
    },
    ...
  ),
),
No ADS
_onNavigateCreateSalesOrderLine()
void _onNavigateCreateSalesOrderLine(
  BuildContext context,
  PrepareItemCreationResult result,
) {
  FlutterArtist.codeFlowLogger.addMethodCall(
    ownerClassInstance: this,
    currentStackTrace: StackTrace.current,
    parameters: null,
  );
  if (result.successForFirst) {
    SalesOrderLine96aDialog.openDialog(context);
  }
}

4. Edit SalesOrderLine

When a user wants to modify a SalesOrderLine, you must set it as the current ITEM and ensure that the associated Form data is successfully loaded before opening the Dialog.
_editSalesOrderLineDetail()
Future<void> _editSalesOrderLineDetail(
  BuildContext context,
  SalesOrderLineInfo salesOrderLine,
) async {
  // SalesOrderLine96aBlock block:
  BlockCurrentItemSettingResult result = await block
      .refreshItemAndSetAsCurrent(item: salesOrderLine, forceLoadForm: true);
  if (!result.successForFirst) {
    return;
  }
  SalesOrderLine96aDialog.openDialog(context);
}
What happens if you don't proactively force the system to load Form info immediately?
If you only ask the system to set the current record without using the forceLoadForm:true parameter, the operation flow will be as follows:
  • ITEM_DETAIL will be loaded via the Block.performLoadItemDetailById() method, but the FormModel will remain in a "pending" state.
  • When the Dialog (containing the FormView) is opened, it only then triggers the loading of Form-related data (e.g., dropdown lists, reference data). Consequently, the user will see a Loading Indicator on the newly opened Dialog.
Using forceLoadForm:true within the refreshItemAndSetAsCurrent method allows us to perform a "Comprehensive Pre-loading" before displaying the edit interface.

5. Create SalesOrder

Similarly to the order lines, when the user clicks the create button on the BlockControlBar of SalesOrder96aBlock, the onNavigateCreate() callback is triggered. Here, you implement the logic to open the SalesOrder96aDialog.
BlockControlBar (salesOrderBlock)
BlockControlBar(
  description: null,
  ownerClassInstance: this,
  block: salesOrderBlock,
  config: BlockControlBarConfig( 
    allowCreateButton: true, 
    onNavigateCreate: (PrepareItemCreationResult result) {
      _onNavigateCreateSalesOrder(context, result);
    },
    ...
  ),
),
_onNavigateCreateSalesOrder()
void _onNavigateCreateSalesOrder(
  BuildContext context,
  PrepareItemCreationResult result,
) {
  FlutterArtist.codeFlowLogger.addMethodCall(
    ownerClassInstance: this,
    currentStackTrace: StackTrace.current,
    parameters: null,
  );
  if (result.successForFirst) {
    SalesOrder96aDialog.openDialog(context);
  }
}
No ADS

FlutterArtist

Show More
No ADS