Created
April 27, 2025 21:10
-
-
Save sethladd/4a33518985bb6bd08e664c435d4ed087 to your computer and use it in GitHub Desktop.
performance art
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; // Or wherever ActionStateType comes from | |
// --- State Pattern Implementation --- | |
// 0. Define the result type (e.g., an enum) if needed for UI logic | |
enum ActionStateType { ready, answering, complete } | |
// 1. Define the State Interface (or abstract class) | |
abstract class GameState { | |
// Method for the state to determine what the *current* state should be | |
// based on the context's data. It returns the type of the state. | |
// It can also trigger transitions by telling the context to change its state object. | |
ActionStateType handleState(GameContext context); | |
// Optional: Define an associated enum type for easy checking | |
ActionStateType get type; | |
} | |
// 2. Define the Context class | |
class GameContext { | |
// Data influencing the state | |
int messageCount; | |
bool isGuessed; | |
// Reference to the current state object | |
late GameState _currentState; | |
// Constructor initializes data and sets the initial state object | |
GameContext({required this.messageCount, required this.isGuessed}) { | |
// Set initial state based on initial data | |
_currentState = _getCorrespondingStateObject(); | |
print("Initial state: ${_currentState.runtimeType}"); | |
} | |
// Method to update the data and potentially trigger a state transition | |
void updateData({required int messageCount, required bool isGuessed}) { | |
this.messageCount = messageCount; | |
this.isGuessed = isGuessed; | |
// Ask the current state object to handle the (potentially new) situation | |
// This might lead to the state object calling back context.setState() | |
_currentState.handleState(this); | |
} | |
// Method called by State objects to change the context's current state | |
void setState(GameState newState) { | |
// Only transition if the new state is actually different | |
if (_currentState.runtimeType != newState.runtimeType) { | |
print("Transitioning from ${_currentState.runtimeType} to ${newState.runtimeType}"); | |
_currentState = newState; | |
} | |
} | |
// Helper to get the state object corresponding to the logical state type | |
GameState _getCorrespondingStateObject() { | |
final stateType = currentActionStateType; // Calculate the logical type | |
switch (stateType) { | |
case ActionStateType.ready: | |
return ReadyState(); | |
case ActionStateType.answering: | |
return AnsweringState(); | |
case ActionStateType.complete: | |
return CompleteState(); | |
} | |
} | |
// Public getter to easily access the *logical* current state type (enum) | |
ActionStateType get currentActionStateType { | |
if (messageCount == 1) return ActionStateType.ready; | |
if (isGuessed || messageCount >= 20) return ActionStateType.complete; | |
return ActionStateType.answering; | |
} | |
// Optional: Expose the state object itself if needed | |
GameState get currentStateObject => _currentState; | |
} | |
// 3. Implement Concrete States | |
class ReadyState implements GameState { | |
@override | |
ActionStateType get type => ActionStateType.ready; | |
@override | |
ActionStateType handleState(GameContext context) { | |
// Check if we should transition *out* of ReadyState | |
if (context.messageCount != 1) { | |
if (context.isGuessed || context.messageCount >= 20) { | |
context.setState(CompleteState()); // Tell context to change state | |
return ActionStateType.complete; | |
} else { | |
context.setState(AnsweringState()); // Tell context to change state | |
return ActionStateType.answering; | |
} | |
} | |
// Otherwise, stay in Ready state | |
return ActionStateType.ready; | |
} | |
} | |
class AnsweringState implements GameState { | |
@override | |
ActionStateType get type => ActionStateType.answering; | |
@override | |
ActionStateType handleState(GameContext context) { | |
// Check if we should transition *out* of AnsweringState | |
if (context.messageCount == 1) { | |
context.setState(ReadyState()); | |
return ActionStateType.ready; | |
} else if (context.isGuessed || context.messageCount >= 20) { | |
context.setState(CompleteState()); | |
return ActionStateType.complete; | |
} | |
// Otherwise, stay in Answering state | |
return ActionStateType.answering; | |
} | |
} | |
class CompleteState implements GameState { | |
@override | |
ActionStateType get type => ActionStateType.complete; | |
@override | |
ActionStateType handleState(GameContext context) { | |
// Check if we should transition *out* of CompleteState | |
// This might happen if conditions change drastically (e.g., reset) | |
if (context.messageCount == 1) { | |
context.setState(ReadyState()); | |
return ActionStateType.ready; | |
} else if (!(context.isGuessed || context.messageCount >= 20)) { | |
// No longer meets completion criteria | |
context.setState(AnsweringState()); | |
return ActionStateType.answering; | |
} | |
// Otherwise, stay in Complete state | |
return ActionStateType.complete; | |
} | |
} | |
// --- Example Usage in a Flutter Widget's State --- | |
class MyWidget extends StatefulWidget { | |
const MyWidget({super.key}); | |
@override | |
State<MyWidget> createState() => _MyWidgetState(); | |
} | |
class _MyWidgetState extends State<MyWidget> { | |
// Assume these are managed elsewhere in your state | |
List<String> messages = ["Initial Message"]; | |
bool guessed = false; | |
// Hold the GameContext instance | |
late GameContext _gameContext; | |
@override | |
void initState() { | |
super.initState(); | |
// Initialize the context with the initial data | |
_gameContext = GameContext( | |
messageCount: messages.length, | |
isGuessed: guessed, | |
); | |
} | |
// Example method to simulate adding a message | |
void _addMessage(String msg) { | |
setState(() { | |
messages.add(msg); | |
// Update the context with the new data | |
_gameContext.updateData( | |
messageCount: messages.length, | |
isGuessed: guessed, | |
); | |
}); | |
} | |
// Example method to simulate guessing correctly | |
void _guessCorrectly() { | |
setState(() { | |
guessed = true; | |
// Update the context | |
_gameContext.updateData( | |
messageCount: messages.length, | |
isGuessed: guessed, | |
); | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
// Get the simple enum state type from the context for UI logic | |
final ActionStateType actionState = _gameContext.currentActionStateType; | |
// You might also access the state object directly if needed: | |
// final GameState currentStateObj = _gameContext.currentStateObject; | |
print("Build method: Current Action State Type: $actionState"); | |
// Use the actionState enum to control UI... | |
String statusText = "Unknown"; | |
switch(actionState) { | |
case ActionStateType.ready: statusText = "Ready to Play!"; break; | |
case ActionStateType.answering: statusText = "Answering (${messages.length}/20)"; break; | |
case ActionStateType.complete: statusText = "Game Complete!"; break; | |
} | |
return MaterialApp( | |
home: Scaffold( | |
appBar: AppBar(title: const Text('State Pattern Example')), | |
body: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Text(statusText, style: Theme.of(context).textTheme.headlineMedium), | |
const SizedBox(height: 20), | |
ElevatedButton( | |
// Disable adding messages if complete or already >= 20 | |
onPressed: actionState == ActionStateType.complete || messages.length >= 20 | |
? null | |
: () => _addMessage("New message ${messages.length + 1}"), | |
child: const Text('Add Message'), | |
), | |
const SizedBox(height: 10), | |
ElevatedButton( | |
// Disable guessing if already complete | |
onPressed: actionState == ActionStateType.complete | |
? null | |
: () => _guessCorrectly(), | |
child: const Text('Guess Correctly'), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
} | |
void main() { | |
runApp(const MyWidget()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment