Skip to content

Learning AFib Efficiently

In order to be useful for prototyping, AFib must be reasonably easy to learn. In addition to the trailer video, the tutorial video, and this guide, the code itself is designed to be easy to learn.

Using YouTube Videos

If you somehow made it this far without viewing the tutorial video, you should start there. It will help you get started efficiently.

Using Project Styles

The starter project styles will help you get off to a quick start. In general, I doubt there is much educational value in starting with the app-starter-minimal style if a more specific style is relevant to your project.

Code Generation

AFib provides the generation command:

dart bin/xxx_afib.dart generate...
This can can be used to generate code for all the key conceptual elements in AFib.

Correctly setting up a new element in AFib sometimes requires modifying multiple files. This command does those modifications for you. I recommend you start by using it for all code generation. You may ultimately stop using it for elements which generate a single file.

You can use dart bin/xxx_afib.dart help generate to see all the generation options. You can also specify a subcommand (e.g. help generate ui or help generate query) to see more detailed help for those subcommands.

Note that some third part components provide their own generate commands. For example, if you used the app-starter-signin-firebase project style, your project is already using afib_firebase_firestore package, which exposes an affs:generate command.

You can use dart bin/xxx_afib.dart help to see all available top-level commands.

Correlation between generate command and file locations

Note that the form of the generate command and the paths of the created files are related. The subcommand after generate is generally the folder in which the file is generated, while often the final suffix of the generated class name is used as a secondary folder. For example:

dart bin/xxx_afib.dart generate ui MyNewScreen

Will yield a file in:

lib/ui/screens/my_new_screen.dart

Similarly:

dart bin/xxx_afib.dart generate state MyModelRoot

Will yield a file in:

lib/state/root/my_model_root.dart

Using Contexts for Discovery

Contexts

Almost all Afib-related code will have access to a context variable. It will either be passed as a parameter to a function you are writing, or will be a member variable in a class you are writing. The context variable provides an API that let's you access all the AFib functionality that is relevant in that context.

Context Documentation

Because the context is your starting point for understanding Afib, I have made a particular effort to document context classes in AFib's API documentation. I recommend you skim through that documentation to get a feel for all the available functionality in each of the different contexts.

Context Typeahead

Although many specialized AFib contexts (e.g. testing contexts) have their own bespoke APIs, the AFib contexts which you will use when writing production code share certain common typeahead prefixes.

Familiarizing yourself with these prefixes gives you a good sense of the core ways you will interact with AFib when writing production code.

context.access...
Used to access state.
context.update...
Used to update state.
context.navigate...
Used to navigate between screens
context.show...
Used to show user interface (e.g. drawers, dialogs, bottom sheets) that does not navigate away from the current screen.
context.execute...
Used to execute some kind of action (described below, usually a query) in AFib.

Debugging in VS Code

Running Your App in the Debugger

You can run your app in the debugger in VS Code by opening lib/main.dart, and clicking the debug text just above the main method.

The main function and debug link

The main function and debug link

Pay attention to the simulator target in the lower right hand corner of VS Code:

The main function and debug link

Emulator selection in VS Code

You can change it by clicking on it.

VS Code Reverting to Another Simulator

Sometimes if you shut down the emulator, VS Code will automatically revert to something else like macos as the environment. If you haven't been actively developing for macos, you may get compiler errors due to out of date podfile dependencies that will confuse you if you think they are new and related to your ongoing development in the previous environment.

App Failing to Start in the Debugger

If you app fails to start within the debugger, try running flutter run from the command line. This may indicate a compiler error in an included library, which VS Code does not show by default, but which flutter run will.

Consider Starting with iOS

Although Flutter is a Google technology, there seem to be several minor hassles when working in the Android emulator -- issues with the device storage not being large enough, and the default Android version being too low. These are all easy to resolve, but you might consider starting on iOS.

Seeing the state

All the state associated with your app is visible in the debugger. Inspecting the state can sometimes help identify bugs. To find the state, inspect context.debugOnlyPublicState or spi.debugOnlyPublicState in the debugger.

For now, the children of the public state that you care about are:

route
Contains information about all the route parameters in the screen hierarchy and the global pool, including any child route parameters.
components
Contains global component state information, including your current XXXState object and any third-party state for your app.

Using Logging

I rarely use logging for debugging because the dart debugger in VS Code is excellent. However, it can occasionally be useful when working with asynchonous processes where stopping in the debugger confuses the order of events.

In that case, all AFib contexts have a log method:

    context.log?.d("The count is $count");

The file lib/initialization/xxx_config.g.dart specifies which logging areas are enabled:

  // --logs-enabled
  //       [//]           Disable all items after this in the logs-enabled array
  //       [af:config]    Logging on any non-test definition/initialization context, and of afib.g.dart/startup configuration values
  //       [af:query]     Internal AFib logging for queries
  //       [af:route]     Internal AFib logging related to routes and navigation
  //       [af:state]     Internal AFib logging related to app state
  //       [af:test]      Internal AFib logging for testing
  //       [af:theme]     Internal AFib logging related to theming
  //       [af:ui]        Internal AFib logging for UI build
  //       [all]          Turn on all logging
  //       [query]        App/third-party logging on AFStartQueryContext.log, AFFinishQuerySuccessContext.log or AFFinishQueryErrorContext.log
  //       [standard]     Logging for app/third-party query and ui, plus afib route and state
  //       [test]         App/third-party logging on test contexts: AFUIPrototypeDefinitionContext.log, AFStateTestDefinitionContext.log, AFDefineTestDataContext.log, AFDefineTestDataContext.log, AFWireframeDefinitionContext.log
  //       [ui]           App/third-party logging for any AFStateProgrammingInterface.log (e.g ...SPI.log) or AFBuildContext.log.
  config.setValue("logs-enabled", ["query", "ui", "test", "//", "af:route", "af:state"]);

Note that the // item disables everything that appears after it in the list, which can be useful for enabling/disabling logging without a lot of typing.

Debugging Your command-line tests

Although you run the command dart bin/xxx_afib.dart test to launch your tests, you should not debug that command in order to debug your tests. It simply updates the xxx_config.g.dart configuration file and executes lib/test/afib/main_afib_test.dart.

Use typeahead to find the main test file

Note that in VS Code, you can find this file with the typeahead main test in the file lookup (Command-p).

To debug your tests, just click the debug text just above the main function in that file.

The main function and debug link

Debug Tests on Command-Line

Note that if you have executed a specific test on the command line, for example:

$ bin/xxx_afib.dart test evex_pr_todoListScreenInitial

The test to run is stored in the xxx_config.g.dart file, and it will be executed when you run debug in the main function.

Some Test Code Runs at Startup

Note that tests in AFib are often implemented as closures which are captured and the executed later by AFib. As a result, the code at the outermost test definition context often runs at startup, capturing the closures, while the code within the closures runs during the tests themselves. If you are not hitting a breakpoint when running a test, try putting the breakpoint deeper in the test code.

Debugging State Tests and UI Tests in Prototype Mode

Note that in additon to running tests from the command line, you can execute both state tests and UI Tests in Prototype mode. This allows you to see the user interface generated by the test, which can be extremely helpful for debugging, and much more efficient that trying to visualize the UI by inspecting variables in the debugger.

The sections on state testing and UI testing explain how to visually debug your tests in more detail.

Debugging Your xxx_afib.dart command

If you develop your own command-line extensions to the bin/xxx_afib.dart command, you may wish to debug it. You can do so by debugging the main function found within that file.

Focusing on a specific UI prototype, state test, or wireframe

You can start AFib directly into a specific UI prototype, state test, or wireframe by changing two files.

First, change the xxx_config.g.dart to AFEnvironment.startupInScreenPrototype, AFEnvironment.startupInStateTest, or AFEnvironment.startupInWireframe respectively.

Then, open prototype.dart, and specify the ID of the UI prototype, state test, or wireframe you wish to startup into, as shown below.

     // use this, plus AFEnvironment.wireframe to start up directly into a wireframe.
     // config.setStartupWireframe(EVEXWireframeID.initial);

     // use this, plus AFEnvironment.stateTest to startup directly into the terminal state of a state test.
     // config.setStartupStateTest(EVEXStateTestID.yourStateTest);

     // use this, plus AFEnvironment.screenPrototype to startup directly into a screen prototype.
     // config.setStartupScreenPrototype(EVEXPrototypeID.searchScreenInitial);