Skip to content

Commit

Permalink
docs: add count warm up example
Browse files Browse the repository at this point in the history
  • Loading branch information
GregoryConrad committed Jul 28, 2023
1 parent 352c598 commit e7861c3
Show file tree
Hide file tree
Showing 22 changed files with 695 additions and 1 deletion.
13 changes: 13 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,16 @@ jobs:
run: melos run analyze --no-select
- name: Run tests
run: melos run test

linux_integration_test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Install dependencies for flutter integration test
run: sudo apt update && sudo apt-get install -y libglu1-mesa ninja-build clang cmake pkg-config libgtk-3-dev liblzma-dev
- uses: pyvista/setup-headless-display-action@v1
- uses: subosito/flutter-action@v2
- uses: bluefireteam/melos-action@v2
- name: Run integration tests
run: melos run test:linux-integration --no-select
7 changes: 7 additions & 0 deletions melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,10 @@ scripts:
packageFilters:
flutter: true
dirExists: test

test:linux-integration:
run: melos exec -c 1 --fail-fast -- "flutter test -d linux integration_test"
description: Run Flutter integration tests for a specific package in this project on linux.
packageFilters:
flutter: true
dirExists: integration_test
28 changes: 28 additions & 0 deletions packages/examples/count_warm_up/integration_test/count_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:count_warm_up/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
testWidgets('counter increments', (tester) async {
await tester.pumpWidget(const CountApp());
await tester.pumpAndSettle();

expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);

await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();

expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
expect(find.text('2'), findsNothing);

await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();

expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsOneWidget);
});
}
125 changes: 125 additions & 0 deletions packages/examples/count_warm_up/lib/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import 'package:flutter/material.dart';
import 'package:flutter_rearch/flutter_rearch.dart';
import 'package:rearch/rearch.dart';
import 'package:shared_preferences/shared_preferences.dart';

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

/// The raw [SharedPreferences] async capsule to be warmed up.
Future<SharedPreferences> sharedPrefsAsyncCapsule(CapsuleHandle _) async {
// Add in a little delay so we can see the loading state
await Future<void>.delayed(const Duration(seconds: 1));
return SharedPreferences.getInstance();
}

/// The warm up capsule for [sharedPrefsAsyncCapsule].
AsyncValue<SharedPreferences> sharedPrefsWarmUpCapsule(CapsuleHandle use) {
final sharedPrefsFuture = use(sharedPrefsAsyncCapsule);
return use.future(sharedPrefsFuture);
}

/// A synchronous copy of [sharedPrefsAsyncCapsule].
SharedPreferences sharedPrefsCapsule(CapsuleHandle use) {
return use(sharedPrefsWarmUpCapsule).dataOrElse(
() => throw StateError('sharedPrefsWarmUpCapsule was not warmed up!'),
);
}

/// A capsule that exposes a persisted count and a [Function] to increment it.
(AsyncValue<int?>, void Function()) persistedCountCapsule(
CapsuleHandle use,
) {
final sharedPrefs = use(sharedPrefsCapsule);
final (count, setCount) = use.persist<int?>(
read: () async => sharedPrefs.getInt('count'),
write: (newCount) async => sharedPrefs.setInt('count', newCount!),
);
final currCount = count.data.asNullable() ?? 0;
return (count, () => setCount(currCount + 1));
}

/// {@template CountApp}
/// The root of the count application.
/// {@endtemplate}
class CountApp extends StatelessWidget {
/// {@macro CountApp}
const CountApp({super.key});

@override
Widget build(BuildContext context) {
return const RearchBootstrapper(
child: MaterialApp(
title: 'Count Warm Up Example',
home: GlobalWarmUps(child: CountBody()),
),
);
}
}

/// {@template GlobalWarmUps}
/// The global warm ups of the application.
/// {@endtemplate}
class GlobalWarmUps extends RearchConsumer {
/// {@macro GlobalWarmUps}
const GlobalWarmUps({required this.child, super.key});

/// The child [Widget] to display when all [Capsule]s are warmed up.
final Widget child;

@override
Widget build(BuildContext context, WidgetHandle use) {
return [
// When any of the following warm up capsules error out,
// the errorBuilder is invoked.
// If any are still loading, the loading widget is shown.
// If there are no AsyncErrors or AsyncLoadings, child is shown.
// Note: all loading happens in parallel automatically
// to speed up your loading times!

use(sharedPrefsWarmUpCapsule),
// other warm ups here...
].toWarmUpWidget(
child: child,
loading: const Center(child: CircularProgressIndicator.adaptive()),
errorBuilder: (errors) => Column(
children: [
// You might want your error display here to be prettier than this.
// You can even wrap the Column in a MaterialApp/Scaffold.
for (final AsyncError(:error, :stackTrace) in errors)
Text('$error\n$stackTrace'),
],
),
);
}
}

/// {@template CountBody}
/// The body of the application.
/// {@endtemplate}
class CountBody extends RearchConsumer {
/// {@macro CountBody}
const CountBody({super.key});

@override
Widget build(BuildContext context, WidgetHandle use) {
final countText = use(persistedCountCapsule)
.$1
.map((count) => count ?? 0)
.map((count) => count.toString());
return Scaffold(
body: Center(
child: switch (countText) {
AsyncData(:final data) =>
Text(data, style: Theme.of(context).textTheme.headlineLarge),
AsyncLoading() => const CircularProgressIndicator.adaptive(),
AsyncError(:final error, :final stackTrace) =>
Text('$error\n$stackTrace')
},
),
floatingActionButton: FloatingActionButton(
onPressed: use(persistedCountCapsule).$2,
child: const Icon(Icons.add),
),
);
}
}
1 change: 1 addition & 0 deletions packages/examples/count_warm_up/linux/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flutter/ephemeral
139 changes: 139 additions & 0 deletions packages/examples/count_warm_up/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)

# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "count_warm_up")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "com.example.count_warm_up")

# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)

# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")

# Root filesystem for cross-building.
if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()

# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Profile" "Release")
endif()

# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
endfunction()

# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
add_subdirectory(${FLUTTER_MANAGED_DIR})

# System-level dependencies.
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)

add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")

# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)

# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})

# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)

# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)

# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
# the default top-level location.
set_target_properties(${BINARY_NAME}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)


# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)


# === Installation ===
# By default, "installing" just makes a relocatable bundle in the build
# directory.
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
endif()

# Start with a clean build bundle directory every time.
install(CODE "
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
" COMPONENT Runtime)

set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")

install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
COMPONENT Runtime)

install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
COMPONENT Runtime)

install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)

foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endforeach(bundled_library)

# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
install(CODE "
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
" COMPONENT Runtime)
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)

# Install the AOT library on non-Debug builds only.
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
Loading

0 comments on commit e7861c3

Please sign in to comment.