mydomain
No ADS
No ADS

FlutterArtist Block External Shelf Event ex1

  1. Structure of the example
  2. SingleSupplierShelf
  3. Supplier27aShelf
  4. Edit Supplier
  5. Create Supplier
  6. Smart Data Refresh Mechanism
In FlutterArtist, when you add, delete, or update an ITEM on a Block within a Shelf, this event can be broadcasted to notify other Shelf(s). This mechanism allows the application to react intelligently through a chain of events:
  • Recipient Block: Automatically performs a re-query to update with the latest data.
  • Recipient Scalar: Automatically refreshes its value.
Note: This article is related to the concepts of FilterInput and SingleItemBlock presented in the articles below:
  • FlutterArtist FilterInput - Ví dụ 1
  • FlutterArtist SingleItemBlock ex1 (***)
We will do a simple example (Demo27a):
  • Supplier27aBlock: Displays the list of suppliers (belongs to Supplier27aShelf).
  • SingleSupplierBlock là một BlockFormModel và thuộc SingleSupperShelf.
To modify a "supplier" of Supplier27aBlock, we call the Form of SingleSupplierBlock. After successfully saving, Supplier27aBlock will be queried again. To do this we need some configuration on Supplier27aBlock and SingleSupplierBlock:
  • Configure so that when adding, removing or editing an ITEM on SingleSupplierBlock an event will be emitted outside the Shelf.
  • Configure Supplier27aBlock to listen for appropriate events outside the Shelf to which it will respond by re-querying.
No ADS
  • Download FlutterArtist Demo
  • FlutterArtist Deferring External Shelf Events (Ex1) ***

1. Structure of the example

The SingleSupplierShelf example is written in a separate article below:
  • FlutterArtist SingleItemBlock ex1 (***)

2. SingleSupplierShelf

On SingleSupplierShelf, we use the emitExternalShelfEvents property to define the type of event to be emitted when data changes.
SingleSupplierShelf > BlockConfig (*)
BlockConfig(
  emitExternalShelfEvents: [
    Event(SupplierData),
  ],
)
  • FlutterArtist SingleItemBlock ex1 (***)
single_supplier_shelf.dart
class SingleSupplierShelf extends Shelf {
  @override
  ShelfStructure defineShelfStructure() {
    return ShelfStructure(
      filterModels: {"single-supplier-filter-model": IntIdFilterModel()},
      blocks: [
        SingleSupplierBlock(
          name: SingleSupplierBlock.blockName,
          description: '',
          config: BlockConfig(
            emitExternalShelfEvents: [Event(SupplierData)], //
          ),
          filterModelName: "single-supplier-filter-model",
          formModel: SingleSupplierFormModel(),
          childBlocks: [],
        ),
      ],
    );
  }

  SingleSupplierBlock findSingleSupplierBlock() {
    return findBlock(SingleSupplierBlock.blockName) as SingleSupplierBlock;
  }
}

3. Supplier27aShelf

On Supplier27aShelf, we configure onExternalShelfEvents to let this Block listen for relevant external signals.
supplier27a_shelf.dart
class Supplier27aShelf extends Shelf {
  @override
  ShelfStructure defineShelfStructure() {
    return ShelfStructure(
      filterModels: {},
      blocks: [
        Supplier27aBlock(
          name: Supplier27aBlock.blkName,
          description: null,
          config: BlockConfig(
            onExternalShelfEvents: const ExternalShelfEventBlockRecipient(
              blockLevelReactionOn: [Event(SupplierData)],
            ),
          ),
          filterModelName: null,
          formModel: null,
          childBlocks: [],
        ),
      ],
    );
  }

  Supplier27aBlock findSupplier27aBlock() {
    return findBlock(Supplier27aBlock.blkName) as Supplier27aBlock;
  }
}

4. Edit Supplier

At Supplier27aShelf:
When the user clicks the "Edit Supplier" button, the _onPressEditSupplierBtn() method will be called:
_onPressEditSupplierBtn()
Future<void> _onPressEditSupplierBtn(BuildContext context) async {
  FlutterArtist.codeFlowLogger.addMethodCall(
    ownerClassInstance: this,
    currentStackTrace: StackTrace.current,
    parameters: null,
  );
  //
  SupplierInfo? supplierInfo = block.currentItem;
  if (supplierInfo == null) {
    return;
  }
  Coordinator coordinator = SingleSupplierEditCoordinator(
    supplierId: supplierInfo.id,
    config: CoordinatorConfig(),
    customNavigate: (BuildContext context, bool success) {
      if (success) {
        Scaffold.of(context).openEndDrawer();
      }
    },
  );
  await coordinator.execute(context);
}
At SingleSupplierShelf:
SingleSupplierEditCoordinator will query SingleSupplierBlock to find the correct record corresponding to the supplierId the user desires and then open SingleSupplierForm on an EndDrawer.
No ADS
single_supplier_edit_coordinator.dart
class SingleSupplierEditCoordinator extends Coordinator {
  final int supplierId;

  SingleSupplierEditCoordinator({
    required this.supplierId,
    required super.config,
    required super.customNavigate,
  });

  @override
  Future<bool> performSetupOperation() async {
    SingleSupplierShelf shelf = FlutterArtist.storage.findShelf();
    SingleSupplierBlock block = shelf.findSingleSupplierBlock();

    IntIdFilterInput filterInput = IntIdFilterInput(idValue: supplierId);
    // With SingleItemBlock, this query return a List with only one item (SupplierData).
    BlockQueryResult result = await block.query(
      filterInput: filterInput,
      afterQueryAction: AfterQueryAction.setAnItemAsCurrentThenLoadForm,
    );
    if (!result.successForAll) {
      return false;
    }
    return true;
  }

  @override
  Future<void> defaultNavigate(BuildContext context, bool success) async {
    if (success) {
      await Get.to(() => SingleSupplierFormScreen());
    }
  }
}
Tip: When you call a ShelfB from ShelfA, you should write code in a Coordinator and place it on the ShelfB side, this helps you to reuse and call ShelfB easily.
No ADS

5. Create Supplier

Similar to the editing scenario, when a user wants to create a new supplier, we coordinate the workflow through the SingleSupplierCreationCoordinator.
At Supplier27aShelf:
When the user clicks the "Create Supplier" button, the _onPressCreateSupplierBtn() method is called to initiate the process.
_onPressCreateSupplierBtn()
Future<void> _onPressCreateSupplierBtn(BuildContext context) async {
  FlutterArtist.codeFlowLogger.addMethodCall(
    ownerClassInstance: this,
    currentStackTrace: StackTrace.current,
    parameters: null,
  );
  //
  Coordinator coordinator = SingleSupplierCreationCoordinator(
    config: CoordinatorConfig(),
    customNavigate: (BuildContext context, bool success) {
      if (success) {
        Scaffold.of(context).openEndDrawer();
      }
    },
  );
  await coordinator.execute(context);
}
At SingleSupplierShelf:
SingleSupplierCreationCoordinator is responsible for preparing the state for the Block and Form, ensuring everything is ready for user input.
single_supplier_creation_coordinator.dart (***)
class SingleSupplierCreationCoordinator extends Coordinator {
  SingleSupplierCreationCoordinator({
    required super.config,
    required super.customNavigate,
  });

  @override
  Future<bool> performSetupOperation() async {
    SingleSupplierShelf shelf = FlutterArtist.storage.findShelf();
    SingleSupplierBlock block = shelf.findSingleSupplierBlock();
  
    // Never been queried
    if (block.dataState == DataState.pending) {
      await block.queryEmpty();
    }
    PrepareItemCreationResult result = await block.prepareFormToCreateItem(
      navigate: null,
    );
  
    if (!result.successForAll) {
      return false;
    }
    return true;
  }

  @override
  Future<void> defaultNavigate(BuildContext context, bool success) async {
    if (success) {
      await Get.to(() => SingleSupplierFormScreen());
    }
  }
}

6. Smart Data Refresh Mechanism

You might wonder: "If Supplier27aBlock is not currently on screen (e.g., in another Tab or a previous page), will it waste resources by re-querying immediately?"
The answer is "No". FlutterArtist optimizes this with the following mechanism:
  • Pending State: When an event is received while the Block is off-screen, it won't query immediately but instead switches its state to DataState.pending.
  • Wake-up on Display: The Block will only re-query if and only if: It has a Block-Context-View currently visible on the screen or any of its child Block(s) (descendants) are visible.
No ADS
No ADS