Skip to content

phase 6 - #55

Merged
grunch merged 3 commits into
mainfrom
004-mostro-p2p-client
Mar 30, 2026
Merged

phase 6#55
grunch merged 3 commits into
mainfrom
004-mostro-p2p-client

Conversation

@grunch

@grunch grunch commented Mar 30, 2026

Copy link
Copy Markdown
Member

Rust:

  • mostro/fsm.rs: 15-state protocol FSM with next_status(status, action,
    role) and 5 unit tests covering happy paths + invalid transitions
  • mostro/actions.rs: new_order, take_buy, take_sell dispatch functions
    that build MostroMessage JSON + NIP-59 Gift Wrap
  • api/orders.rs: create_order with full param validation (fiat_amount
    XOR range, fiat_code/payment_method non-empty, range min > 0 < max)
  • api/types.rs: NewOrderParams struct

Dart:

  • AddOrderButton: expandable FAB with Buy/Sell sub-buttons, dark
    overlay, animated rotation + scale transitions
  • AddOrderScreen: 4-card form (type+amount+currency, payment methods,
    price type, premium slider), Cancel/Submit bottom bar with validation
  • CurrencySection: tappable selector with search dialog from fiat.json
  • PaymentMethodSection: multi-select chips + custom text field
  • PriceSection: Market/Fixed toggle, purple premium slider with
    editable field, fixed sats input
  • app_routes: wired AddOrderScreen with ?type= query parameter
  • home_screen: replaced simple FAB with AddOrderButton

Summary by CodeRabbit

  • New Features

    • Order creation interface with expandable Buy/Sell floating action button
    • Currency selection with search and country flag display
    • Payment method management supporting custom entries
    • Market premium and fixed satoshi pricing mode toggle
    • Order amount or range (min/max) specification
    • Backend order creation with comprehensive validation
    • Mostro protocol messaging for order publishing
  • Refactor

    • Consolidated floating action button into reusable shared component

grunch added 2 commits March 30, 2026 01:26
…ble FAB

Rust:
- mostro/fsm.rs: 15-state protocol FSM with next_status(status, action,
  role) and 5 unit tests covering happy paths + invalid transitions
- mostro/actions.rs: new_order, take_buy, take_sell dispatch functions
  that build MostroMessage JSON + NIP-59 Gift Wrap
- api/orders.rs: create_order with full param validation (fiat_amount
  XOR range, fiat_code/payment_method non-empty, range min > 0 < max)
- api/types.rs: NewOrderParams struct

Dart:
- AddOrderButton: expandable FAB with Buy/Sell sub-buttons, dark
  overlay, animated rotation + scale transitions
- AddOrderScreen: 4-card form (type+amount+currency, payment methods,
  price type, premium slider), Cancel/Submit bottom bar with validation
- CurrencySection: tappable selector with search dialog from fiat.json
- PaymentMethodSection: multi-select chips + custom text field
- PriceSection: Market/Fixed toggle, purple premium slider with
  editable field, fixed sats input
- app_routes: wired AddOrderScreen with ?type= query parameter
- home_screen: replaced simple FAB with AddOrderButton
…cycle,

dialog context, FSM safety, fiat_amount validation

Rust:
- actions.rs: remove unused imports (anyhow!, OrderInfo); fix doc
- orders.rs: validate fixed fiat_amount > 0 and is_finite
- fsm.rs: restrict NewOrder to Pending only; add cooperative cancel
  role enforcement comment

Dart:
- add_order_screen: ref.watch() for reactive Submit button
- price_section: ConsumerStatefulWidget with managed controller
- payment_method_section: ConsumerStatefulWidget with managed controller
- currency_section + payment_method_section: dialog context for pop
@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@grunch has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 42 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 18 minutes and 42 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cbd6a12c-f4e6-435e-85cf-e40ffff707a0

📥 Commits

Reviewing files that changed from the base of the PR and between 486d96e and 2fe6f84.

📒 Files selected for processing (5)
  • lib/features/order/screens/add_order_screen.dart
  • lib/features/order/widgets/price_section.dart
  • rust/src/api/orders.rs
  • rust/src/mostro/actions.rs
  • rust/src/mostro/fsm.rs

Walkthrough

This PR implements order creation functionality across the Flutter frontend and Rust backend. It introduces a new AddOrderScreen with supporting widgets for currency, payment methods, and pricing configuration; an expandable floating action button for order creation; Rust API validation for order parameters; and Mostro protocol state machine and action builders for order lifecycle management.

Changes

Cohort / File(s) Summary
Flutter Routing
lib/core/app_routes.dart, lib/features/home/screens/home_screen.dart
Updated addOrder route to instantiate AddOrderScreen with dynamic orderType from query parameters; replaced inline FAB in HomeScreen with shared AddOrderButton widget.
Order Creation Screen
lib/features/order/screens/add_order_screen.dart
New ConsumerStatefulWidget implementing full order creation form with text controllers, range-mode toggle, form validation, and async submit flow with loading state and mounted check.
Order Form Widgets
lib/features/order/widgets/currency_section.dart, lib/features/order/widgets/payment_method_section.dart, lib/features/order/widgets/price_section.dart
Three new Riverpod-driven widgets: CurrencySection with searchable picker dialog, PaymentMethodSection with selectable chips and custom method input, PriceSection with market/fixed pricing toggle and slider/text inputs.
Shared FAB Widget
lib/shared/widgets/add_order_button.dart
New expandable floating action button with animated sub-actions ("Buy"/"Sell"), overlay backdrop, navigation to addOrder route with type query parameter, and icon/color state transitions.
Rust API Types & Functions
rust/src/api/types.rs, rust/src/api/orders.rs
Added NewOrderParams struct with fiat amount (single/range), currency, payment method, and premium fields; implemented create_order() function with exclusive-or validation, UUID generation, timestamp assignment, in-memory persistence.
Rust Mostro Protocol
rust/src/mostro/actions.rs, rust/src/mostro/fsm.rs, rust/src/mostro/mod.rs
New actions.rs module building NIP-59 Gift Wrap payloads for order creation/take operations; new fsm.rs implementing Action enum and next_status() state machine logic for order lifecycle transitions; exposed both modules via mod.rs.
Documentation
specs/004-mostro-p2p-client/tasks.md
Marked tasks T038–T046 as completed, indicating order creation feature implementation progress.

Sequence Diagram

sequenceDiagram
    actor User
    participant AddOrderScreen as AddOrderScreen<br/>(Flutter)
    participant API as orders API<br/>(Rust)
    participant FSM as Mostro FSM<br/>(Rust)
    participant Cache as Order Cache<br/>(In-Memory)

    User->>AddOrderScreen: Fill order form & submit
    AddOrderScreen->>AddOrderScreen: Validate inputs
    AddOrderScreen->>API: create_order(params)
    API->>API: Validate params (XOR, ranges, non-empty)
    API->>FSM: Check initial action allowed
    FSM-->>API: Status transition: Pending
    API->>Cache: upsert_order(OrderInfo)
    Cache-->>API: Order persisted
    API-->>AddOrderScreen: OrderInfo returned
    AddOrderScreen->>AddOrderScreen: Set loading=false
    AddOrderScreen->>User: Navigate to orderBook
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 Order forms now bloom with UI grace,
Range toggles and currencies find their place,
Mostro's FSM hops through states,
From Pending to trades, no delays or waits—
The marketplace grows, one hop at a time! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'phase 6' is too vague and generic; it references a phase number without conveying what feature or functionality is being implemented. Use a more descriptive title that captures the main change, such as 'Add create-order flow with FAB, form, and Mostro FSM' or 'Implement phase 6: order creation UI and backend API'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 004-mostro-p2p-client

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@grunch grunch changed the title 004 mostro p2p client phase 6 Mar 30, 2026
@grunch

grunch commented Mar 30, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Mar 30, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
lib/features/order/widgets/price_section.dart (1)

51-57: Consider using addListener instead of syncing in build.

Writing to _premiumController.text inside build() works but mixes concerns. A cleaner approach would be to use a ref.listen in initState (via WidgetsBinding.instance.addPostFrameCallback) or in a ref.listen callback to update the controller when the provider changes.

That said, the current approach is functional and the _editingPremium guard prevents feedback loops.

Also applies to: 170-179

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/features/order/widgets/price_section.dart` around lines 51 - 57, The
build() method is mutating _premiumController.text to reflect provider changes
which mixes concerns; instead, stop writing to _premiumController in build and
move the sync logic into a lifecycle/listener: set up a ref.listen (or a
provider listener) in initState (or via
WidgetsBinding.instance.addPostFrameCallback) to observe the provider value and
update _premiumController.text only when !_editingPremium and the text differs.
Update both places that write in build (the block around _premiumController.text
and the similar block at lines 170-179) to remove that mutation and rely on the
new ref.listen handler; keep the _editingPremium guard and ensure you
cancel/unsubscribe the listener in dispose if needed.
rust/src/mostro/actions.rs (2)

12-13: Consider consolidating MOSTRO_DM_KIND with KIND_ORDER.

rust/src/nostr/order_events.rs already defines pub const KIND_ORDER: u16 = 38383. Using that constant here would avoid duplication.

-/// Kind used for Mostro direct messages (NIP-59 inner rumor).
-const MOSTRO_DM_KIND: u16 = 38383;
+use crate::nostr::order_events::KIND_ORDER;

Then replace MOSTRO_DM_KIND with KIND_ORDER in the gift_wrap::wrap calls.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/src/mostro/actions.rs` around lines 12 - 13, Remove the duplicated const
MOSTRO_DM_KIND and replace its usages in gift_wrap::wrap calls with the existing
KIND_ORDER constant; update the imports or qualify the reference (e.g., use
crate::nostr::order_events::KIND_ORDER or refer to order_events::KIND_ORDER) so
gift_wrap::wrap(...) passes KIND_ORDER instead of MOSTRO_DM_KIND and delete the
now-unused MOSTRO_DM_KIND definition.

56-112: take_buy and take_sell have nearly identical implementations.

These two functions differ only in the "action" string value. Consider extracting a shared helper to reduce duplication.

♻️ Suggested refactor
async fn take_order_impl(
    sender_keys: &Keys,
    mostro_pubkey: &PublicKey,
    order_id: &str,
    amount: Option<f64>,
    action: &str,
) -> Result<String> {
    let mut content = json!({ "id": order_id });
    if let Some(amt) = amount {
        content["amount"] = json!(amt);
    }

    let payload = json!({
        "order": {
            "version": 1,
            "action": action,
            "content": content,
        }
    });

    gift_wrap::wrap(
        sender_keys,
        mostro_pubkey,
        &payload.to_string(),
        Kind::from(MOSTRO_DM_KIND),
    )
    .await
}

pub async fn take_buy(
    sender_keys: &Keys,
    mostro_pubkey: &PublicKey,
    order_id: &str,
    amount: Option<f64>,
) -> Result<String> {
    take_order_impl(sender_keys, mostro_pubkey, order_id, amount, "take-buy").await
}

pub async fn take_sell(
    sender_keys: &Keys,
    mostro_pubkey: &PublicKey,
    order_id: &str,
    amount: Option<f64>,
) -> Result<String> {
    take_order_impl(sender_keys, mostro_pubkey, order_id, amount, "take-sell").await
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/src/mostro/actions.rs` around lines 56 - 112, Both take_buy and
take_sell duplicate the same payload-building and wrapping logic; extract a
single helper (e.g. take_order_impl) that accepts sender_keys: &Keys,
mostro_pubkey: &PublicKey, order_id: &str, amount: Option<f64>, and action:
&str, builds the JSON payload (including version, action, and content with
optional "amount"), calls gift_wrap::wrap(..., Kind::from(MOSTRO_DM_KIND)).await
and returns Result<String>, then have take_buy and take_sell simply call that
helper with "take-buy" and "take-sell" respectively.
rust/src/api/types.rs (1)

211-229: Duplicate NewOrderParams struct exists in mostro/actions.rs.

This struct has an identical definition in rust/src/mostro/actions.rs (lines 16-26). This creates a maintenance burden and potential for drift. Consider:

  1. Using this single definition from api::types in mostro::actions, or
  2. Adding a From impl if the structs need to remain separate for layering reasons.

The relevant snippet from rust/src/mostro/actions.rs confirms field-for-field equivalence.

♻️ Suggested approach: reuse api::types::NewOrderParams

In rust/src/mostro/actions.rs, replace the local struct with an import:

-/// Parameters for creating a new order.
-#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
-pub struct NewOrderParams {
-    pub kind: OrderKind,
-    pub fiat_amount: Option<f64>,
-    pub fiat_amount_min: Option<f64>,
-    pub fiat_amount_max: Option<f64>,
-    pub fiat_code: String,
-    pub payment_method: String,
-    pub premium: f64,
-    pub amount_sats: Option<u64>,
-}
+use crate::api::types::NewOrderParams;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/src/api/types.rs` around lines 211 - 229, There is a duplicate
NewOrderParams struct defined in api::types and mostro::actions; remove the copy
in mostro::actions and reuse api::types::NewOrderParams by importing it (use
crate::api::types::NewOrderParams) or, if you need distinct types for layering,
keep the local MostroNewOrderParams and add a From<api::types::NewOrderParams>
(and/or Into) impl to convert between them; update any references in functions
(e.g., those constructing or accepting NewOrderParams in mostro::actions) to use
the chosen single definition or conversion helpers to eliminate drift.
rust/src/mostro/fsm.rs (1)

105-108: Consider extending buy_order_happy_path test coverage.

This test only verifies the initial TakeSell transition. For consistency with sell_order_happy_path, consider extending it through the full flow: WaitingPayment → Active → FiatSent → SettledHoldInvoice.

✨ Extended test
 #[test]
 fn buy_order_happy_path() {
     assert_eq!(next_status(&OrderStatus::Pending, Action::TakeSell, TradeRole::Seller), Some(OrderStatus::WaitingPayment));
+    // Continue through the flow - seller pays invoice
+    assert_eq!(next_status(&OrderStatus::WaitingPayment, Action::PayInvoice, TradeRole::Seller), Some(OrderStatus::Active));
+    // Buyer marks fiat sent
+    assert_eq!(next_status(&OrderStatus::Active, Action::FiatSent, TradeRole::Buyer), Some(OrderStatus::FiatSent));
+    // Seller releases
+    assert_eq!(next_status(&OrderStatus::FiatSent, Action::Release, TradeRole::Seller), Some(OrderStatus::SettledHoldInvoice));
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/src/mostro/fsm.rs` around lines 105 - 108, Extend the
buy_order_happy_path test to assert the full lifecycle: after the existing
assert that TakeSell from Pending (TradeRole::Seller) yields WaitingPayment,
call next_status repeatedly to verify WaitingPayment → Active → FiatSent →
SettledHoldInvoice, using the same Action variants and
TradeRole::Buyer/TradeRole::Seller roles used in the sell_order_happy_path test;
add asserts for each intermediate state (e.g.,
assert_eq!(next_status(&OrderStatus::WaitingPayment, <appropriate Action>,
<role>), Some(OrderStatus::Active)), then for Active → FiatSent and FiatSent →
SettledHoldInvoice) so the test covers the entire happy path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/features/order/screens/add_order_screen.dart`:
- Around line 24-39: The screen's global provider state is not cleared on entry,
so in _AddOrderScreenState.initState you should reset the listed providers
(selectedPaymentMethodsProvider, customPaymentMethodProvider,
selectedFiatCodeProvider, isMarketPriceProvider, premiumValueProvider,
fixedSatsProvider) to their defaults via their notifiers/readers (e.g., call
ref.read(<provider>.notifier).reset() or assign the default value with
ref.read(<provider>.notifier).state = <default>) before any UI uses them; add
this reset logic inside initState of _AddOrderScreenState so each new
AddOrderScreen starts with fresh/default provider state.

In `@lib/features/order/widgets/price_section.dart`:
- Around line 142-154: The onTapOutside handler currently sets _editingPremium =
false without triggering a rebuild, so the TextField won't refresh to reflect
slider/premiumValueProvider changes; update the onTapOutside callback in the
widget containing _editingPremium (the same place as onTap/onSubmitted) to call
setState(() { _editingPremium = false; }); and also ensure after setState you
sync the TextEditingController text from ref.read(premiumValueProvider) (or
trigger the same update logic used when slider changes) so the field shows the
latest clamped double value.

In `@rust/src/api/orders.rs`:
- Around line 165-173: The range validation block (when has_range is true) for
fiat_amount uses params.fiat_amount_min.unwrap() and fiat_amount_max.unwrap()
but doesn't check for non-finite values; update the validation in the same scope
(the block that reads min and max) to first verify both min.is_finite() and
max.is_finite() and return an Err(anyhow::anyhow!(...)) with a clear message if
either is not finite, then continue with the existing min <= 0.0 || min >= max
check; this change should be applied where has_range, params.fiat_amount_min,
and params.fiat_amount_max are referenced in rust/src/api/orders.rs.

---

Nitpick comments:
In `@lib/features/order/widgets/price_section.dart`:
- Around line 51-57: The build() method is mutating _premiumController.text to
reflect provider changes which mixes concerns; instead, stop writing to
_premiumController in build and move the sync logic into a lifecycle/listener:
set up a ref.listen (or a provider listener) in initState (or via
WidgetsBinding.instance.addPostFrameCallback) to observe the provider value and
update _premiumController.text only when !_editingPremium and the text differs.
Update both places that write in build (the block around _premiumController.text
and the similar block at lines 170-179) to remove that mutation and rely on the
new ref.listen handler; keep the _editingPremium guard and ensure you
cancel/unsubscribe the listener in dispose if needed.

In `@rust/src/api/types.rs`:
- Around line 211-229: There is a duplicate NewOrderParams struct defined in
api::types and mostro::actions; remove the copy in mostro::actions and reuse
api::types::NewOrderParams by importing it (use
crate::api::types::NewOrderParams) or, if you need distinct types for layering,
keep the local MostroNewOrderParams and add a From<api::types::NewOrderParams>
(and/or Into) impl to convert between them; update any references in functions
(e.g., those constructing or accepting NewOrderParams in mostro::actions) to use
the chosen single definition or conversion helpers to eliminate drift.

In `@rust/src/mostro/actions.rs`:
- Around line 12-13: Remove the duplicated const MOSTRO_DM_KIND and replace its
usages in gift_wrap::wrap calls with the existing KIND_ORDER constant; update
the imports or qualify the reference (e.g., use
crate::nostr::order_events::KIND_ORDER or refer to order_events::KIND_ORDER) so
gift_wrap::wrap(...) passes KIND_ORDER instead of MOSTRO_DM_KIND and delete the
now-unused MOSTRO_DM_KIND definition.
- Around line 56-112: Both take_buy and take_sell duplicate the same
payload-building and wrapping logic; extract a single helper (e.g.
take_order_impl) that accepts sender_keys: &Keys, mostro_pubkey: &PublicKey,
order_id: &str, amount: Option<f64>, and action: &str, builds the JSON payload
(including version, action, and content with optional "amount"), calls
gift_wrap::wrap(..., Kind::from(MOSTRO_DM_KIND)).await and returns
Result<String>, then have take_buy and take_sell simply call that helper with
"take-buy" and "take-sell" respectively.

In `@rust/src/mostro/fsm.rs`:
- Around line 105-108: Extend the buy_order_happy_path test to assert the full
lifecycle: after the existing assert that TakeSell from Pending
(TradeRole::Seller) yields WaitingPayment, call next_status repeatedly to verify
WaitingPayment → Active → FiatSent → SettledHoldInvoice, using the same Action
variants and TradeRole::Buyer/TradeRole::Seller roles used in the
sell_order_happy_path test; add asserts for each intermediate state (e.g.,
assert_eq!(next_status(&OrderStatus::WaitingPayment, <appropriate Action>,
<role>), Some(OrderStatus::Active)), then for Active → FiatSent and FiatSent →
SettledHoldInvoice) so the test covers the entire happy path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4052927e-0291-4623-a56a-09051c1d4c94

📥 Commits

Reviewing files that changed from the base of the PR and between 8303a84 and 486d96e.

📒 Files selected for processing (13)
  • lib/core/app_routes.dart
  • lib/features/home/screens/home_screen.dart
  • lib/features/order/screens/add_order_screen.dart
  • lib/features/order/widgets/currency_section.dart
  • lib/features/order/widgets/payment_method_section.dart
  • lib/features/order/widgets/price_section.dart
  • lib/shared/widgets/add_order_button.dart
  • rust/src/api/orders.rs
  • rust/src/api/types.rs
  • rust/src/mostro/actions.rs
  • rust/src/mostro/fsm.rs
  • rust/src/mostro/mod.rs
  • specs/004-mostro-p2p-client/tasks.md

Comment thread lib/features/order/screens/add_order_screen.dart
Comment thread lib/features/order/widgets/price_section.dart Outdated
Comment thread rust/src/api/orders.rs
…ync,

range validation, dedup, test coverage

Rust:
- orders.rs: add is_finite check for range fiat_amount_min/max
- actions.rs: remove duplicate NewOrderParams (use api::types), replace
  MOSTRO_DM_KIND with KIND_ORDER from order_events, extract
  take_order_impl helper to deduplicate take_buy/take_sell
- fsm.rs: extend buy_order_happy_path test to cover full lifecycle
  (WaitingPayment → Active → FiatSent → SettledHoldInvoice)

Dart:
- add_order_screen: reset all form providers in initState via
  Future.microtask so each screen starts fresh
- price_section: use ref.listen for controller sync instead of mutating
  in build; wrap _editingPremium changes in setState; sync controller
  text in onTapOutside
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant