0

I'm new to flutter and I wish to organize my folders in order to write cleaner code. I'm trying to divide my flutter page on three parts:

  1. login_view.dart (this view will be rendered as the login view and will call functions defined into login_builder.dart to build each of its widgets)
  2. login_builder.dart (contains function definitions called by login_view.dart to build widgets)
  3. login_state.dart (for state management)

But when I call Provider.of(context) inside a functiton that is defined into login_builder.dart (out of the login_view.dart widget tree) it always throws ProviderNotFoundException

// login_view.dart

import 'package:discover/ui/views/login/login_builder.dart'; import 'package:discover/ui/views/login/login_state.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class Login extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider<LoginState>( create: (context) => LoginState(), child: buildLoginForm(context), ); } } 

// login_builder.dart

import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:discover/ui/views/login/login_state.dart'; Widget buildLoginForm(BuildContext context) { var loginState = Provider.of<LoginState>(context); return Form( child: Column( children: <Widget>[ TextFormField( onChanged: (value) => loginState.userName = value, ) ], ), ); } 

// login_state.dart

import 'package:flutter/material.dart'; class LoginState extends ChangeNotifier { String _userName = ""; String get userName => _userName; set userName(String userName) { _userName = userName; } } 

// Debug Console

════════ Exception caught by widgets library ═══════════════════════════════════ The following ProviderNotFoundException was thrown building Login(dirty): Error: Could not find the correct Provider<LoginState> above this Login Widget To fix, please: * Ensure the Provider<LoginState> is an ancestor to this Login Widget * Provide types to Provider<LoginState> * Provide types to Consumer<LoginState> * Provide types to Provider.of<LoginState>() * Ensure the correct `context` is being used. If none of these solutions work, please file a bug at: https://github.com/rrousselGit/provider/issues The relevant error-causing widget was Login When the exception was thrown, this was the stack Provider.of buildLoginForm Login.build StatelessElement.build ComponentElement.performRebuild 
1
  • It doesn't work like that. It's still at the same level where you created the provider. It actually needs to be a child to access the context. Commented May 9, 2020 at 18:37

1 Answer 1

1

The context you passed to login_builder is the same context that was passed to login_view, so it exists in a place of the widget tree above where you inserted your ChangeNotifierProvider which is why you can't find it with Provider.of. In order to get this to work the way you want it to, you need to utilize a Builder widget to gain a new BuildContext that exists below the provider:

class Login extends StatelessWidget { @override Widget build(BuildContext context) { return ChangeNotifierProvider<LoginState>( create: (context) => LoginState(), child: Builder(builder: buildLoginForm), ); } } 

Now the context passed to buildLoginForm will be the context for the Builder (which exists below the provider) instead of for your Login widget (which exists above the provider).

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, It works now. The last thing I want to know is if my architecture a good practice ? If there is a better way to organise the code ? It would be great to know before diving deeper.
@MouhammadDiakhate It's standard practice to separate UI and logic as well as to break up the UI into smaller more modular chunks. The way you are doing it I could see argued is somewhat arbitrary since the outer shell exists only to provide the inner layer with a state and provider. I'd suggest instead of an external build method, you could implement login_builder as a StatelessWidget in its own right. That would eliminate the need to make sure you are passing in the correct context and make your UI declarations cleaner overall (no more depending on an esoteric Builder widget).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.