简体   繁体   English

小部件测试失败,未找到 MediaQuery 小部件

[英]Widget test fails with No MediaQuery widget found

My question is about flutter widget test, what is proper way to test existing widgets wrapped new Scaffold(...)?我的问题是关于 flutter 小部件测试,测试现有小部件包装新脚手架(...)的正确方法是什么? I have found MediaQuery.of but it accepts BuildContext instead of Widget .我找到MediaQuery.of但它接受BuildContext而不是Widget

Details: I have wrote simple login form widget and trying to implement widget test for it.详细信息:我已经编写了简单的登录表单小部件并尝试为其实现小部件测试。 After executing test i got exception:执行测试后我得到异常:

Expected: 'Sorry, only customer can login from mobile device. [Mock]'
  Actual: FlutterError:<No MediaQuery widget found.
          Scaffold widgets require a MediaQuery widget ancestor.
          The specific widget that could not find a MediaQuery ancestor was:
            Scaffold-[LabeledGlobalKey<ScaffoldState>#8ffee]
          The ownership chain for the affected widget is:
            Scaffold-[LabeledGlobalKey<ScaffoldState>#8ffee] ← LoginForm ← [root]
          Typically, the MediaQuery widget is introduced by the MaterialApp or WidgetsApp widget at
          the top of your application widget tree.>
   Which: FlutterError:<No MediaQuery widget found.
          Scaffold widgets require a MediaQuery widget ancestor.
          The specific widget that could not find a MediaQuery ancestor was:
            Scaffold-[LabeledGlobalKey<ScaffoldState>#8ffee]
          The ownership chain for the affected widget is:
            Scaffold-[LabeledGlobalKey<ScaffoldState>#8ffee] ← LoginForm ← [root]
          Typically, the MediaQuery widget is introduced by the MaterialApp or WidgetsApp widget at
          the top of your application widget tree.>is not a string

When the exception was thrown, this was the stack:
#4      main.<anonymous closure> (C:\Work\app_mobile\test\login_widget_test.dart:21:5)
<asynchronous suspension>
#5      testWidgets.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:flutter_test\src\widget_tester.dart:61:25)
#6      TestWidgetsFlutterBinding._runTestBody (package:flutter_test\src\binding.dart:471:19)
<asynchronous suspension>
#9      TestWidgetsFlutterBinding._runTest (package:flutter_test\src\binding.dart:458:14)
#10     AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test\src\binding.dart:640:24)
#11     _FakeAsync.run.<anonymous closure> (package:quiver\testing\src\async\fake_async.dart:186:24)
#15     _FakeAsync.run (package:quiver\testing\src\async\fake_async.dart:185:11)
#16     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test\src\binding.dart:638:16)
#17     testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test\src\widget_tester.dart:60:24)
#18     Declarer.test.<anonymous closure>.<anonymous closure> (package:test\src\backend\declarer.dart:160:19)
<asynchronous suspension>
#19     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test\src\backend\invoker.dart:206:15)
<asynchronous suspension>
#23     Invoker.waitForOutstandingCallbacks (package:test\src\backend\invoker.dart:203:5)
#24     Declarer.test.<anonymous closure> (package:test\src\backend\declarer.dart:158:29)
<asynchronous suspension>
#25     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test\src\backend\invoker.dart:351:23)
<asynchronous suspension>
#27     StackZoneSpecification._run (package:stack_trace\src\stack_zone_specification.dart:209:15)
#28     StackZoneSpecification._registerCallback.<anonymous closure> (package:stack_trace\src\stack_zone_specification.dart:119:48)
#33     StackZoneSpecification._run (package:stack_trace\src\stack_zone_specification.dart:209:15)
#34     StackZoneSpecification._registerCallback.<anonymous closure> (package:stack_trace\src\stack_zone_specification.dart:119:48)
#39     _Timer._runTimers (dart:isolate-patch/dart:isolate/timer_impl.dart:367)
#40     _Timer._handleMessage (dart:isolate-patch/dart:isolate/timer_impl.dart:401)
#41     _RawReceivePortImpl._handleMessage (dart:isolate-patch/dart:isolate/isolate_patch.dart:163)
(elided 17 frames from package dart:async and package dart:async-patch)

Here is Login form widget:这是登录表单小部件:

import 'dart:async';
import 'dart:convert';

import 'package:app_facade/app_facade.dart';
import 'package:app_mobile/utils/dependency_injection.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/services.dart';

class LoginForm extends StatefulWidget {
  const LoginForm({ Key key }) : super(key: key);

  static GlobalKey<FormFieldState<String>> emailFieldKey = new GlobalKey<FormFieldState<String>>();
  static GlobalKey<FormFieldState<String>> passwordFieldKey = new GlobalKey<FormFieldState<String>>();
  static const String routeName = '/';

  @override
  LoginFormState createState() => new LoginFormState();
}

class LoginData {
  String email = '';
  String password = '';
}

class LoginFormState extends State<LoginForm> {
  final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

  LoginData loginData = new LoginData();

  UserApi _userApi;

  void showInSnackBar(String value) {
    _scaffoldKey.currentState.showSnackBar(new SnackBar(
        content: new Text(value)
    ));
  }

  bool _autovalidate = false;
  bool _formWasEdited = false;
  final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();


  @override
  void initState() {
    super.initState();
    _userApi = new Injector().userApi;
  }

  Future<Null> _handleSubmitted() async {
    final FormState form = _formKey.currentState;
    if (!form.validate()) {
      _autovalidate = true;  // Start validating on every change.
      showInSnackBar('Please fix the errors in red before submitting.');
    } else {
      form.save();
      login();
    }
  }

  Future<Null> login() async {
    try {
      await _userApi.login(loginData.email, loginData.password);
      Navigator.popAndPushNamed(context, '/user');
    } catch (e) {
      showInSnackBar(e.toString());
    }
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      key: _scaffoldKey,
      appBar: new AppBar(
        title: const Text('Some'),
      ),
      body: new Form(
          key: _formKey,
          autovalidate: _autovalidate,
          child: new ListView(
            padding: const EdgeInsets.symmetric(horizontal: 16.0),
            children: <Widget>[
              new TextFormField(
                key: new Key('email'),
                decoration: const InputDecoration(
                  icon: const Icon(Icons.person),
                  hintText: 'Your email',
                  labelText: 'Email *',
                ),
                onSaved: (String value) { loginData.email = value; },
              ),
              new TextFormField(
                key: LoginForm.passwordFieldKey,
                decoration: const InputDecoration(
                  icon: const Icon(Icons.security),
                  hintText: 'Your password',
                  labelText: 'Password *',
                  ),
                obscureText: true,
                onSaved: (String value) { loginData.password = value; },
              ),
              new Container(
                padding: const EdgeInsets.all(20.0),
                alignment: const FractionalOffset(0.5, 0.5),
                child: new RaisedButton(
                  child: const Text('SUBMIT'),
                  onPressed: _handleSubmitted,
                ),
              ),
              new Container(
                padding: const EdgeInsets.only(top: 20.0),
                child: new Text('* indicates required field', style: Theme.of(context).textTheme.caption),
              ),
            ],
          )
      ),
    );
  }
}

And here is widget test:这是小部件测试:

import 'package:app_facade/app_facade.dart';
import 'package:app_mobile/login_form.dart';
import 'package:app_mobile/utils/dependency_injection.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
  testWidgets('login widget test', (WidgetTester tester) async {
    Injector.configure(BackendType.MOCK);
    // Tells the tester to build a UI based on the widget tree passed to it
    var loginForm = new LoginForm();
    await tester.pumpWidget(
      loginForm
    );

    tester.enterText(find.byKey(LoginForm.emailFieldKey), "login");
    tester.enterText(find.byKey(LoginForm.passwordFieldKey), "password");

    var exception = tester.takeException();
    print(exception);
    expect(exception, equals('Sorry, only customer can login from mobile device. [Mock]'));
  });
}

I have found MediaQuery.of but don't understand how can it be used with existing widget?我找到MediaQuery.of但不明白它如何与现有小部件一起使用? It accept BuildContext as parameter.它接受BuildContext作为参数。

You need to wrap your widget with the MediaQuery(...) instance, and because you are using Scaffold(..) you must wrap it in a MaterialApp(..)您需要使用MediaQuery(...)实例包装您的小部件,并且由于您使用的是Scaffold(..)您必须将其包装在MaterialApp(..)

Read more about MediaQuery 阅读有关 MediaQuery 的更多信息

Example:示例:

Widget testWidget = new MediaQuery(
      data: new MediaQueryData(),
      child: new MaterialApp(home: new LoginForm())
)

I had same problem and I also had to wrap it in MaterialApp, but I did that in a bit other way, without using MediaQuery.我遇到了同样的问题,我也不得不将它包装在 MaterialApp 中,但我以另一种方式做到了这一点,没有使用 MediaQuery。 In my case it works在我的情况下它有效

void main() {

Widget createWidgetForTesting({Widget child}){
return MaterialApp(
  home: child,
);
}

testWidgets('Login Page smoke test', (WidgetTester tester) async {

await tester.pumpWidget(createWidgetForTesting(child: new LoginPage()));

await tester.pumpAndSettle();

});
}

**A perfect example of how a scaffold() widget should be the child of a MaterialApp() ** ** scaffold() 小部件应该如何成为 MaterialApp() 的子组件的完美示例 **

          import 'package:flutter/material.dart';
   
         class MyApp extends StatelessWidget {

             @override
          Widget build(BuildContext context) {
        return MaterialApp(
         debugShowCheckedModeBanner: false,
           theme: ThemeData(
      primarySwatch: Colors.blue,
      ),
     home: MyHomePage(title: 'title'),
    );
  }
}

        class MyHomePage extends StatefulWidget {
        MyHomePage({Key Key, this.title}) : super(key: Key);

       @override
        _MyHomePageState createState() => _MyHomePageState();

    }

        class _MyHomePageState extends State<MyHomePage> {
    Widget build(BuildContext context) {
      return new Scaffold(
     appBar: AppBar(
    backgroundColor: Colors.transparent,
    title: widget.title
 )

}

I have also faced the same issue and solved by the below method.我也遇到了同样的问题并通过以下方法解决。

Note: I was using bloc.注意:我使用的是 bloc。

void main() {
 testWidgets('Find an app bar with name of weather search',
  (WidgetTester tester) async {
 await tester.pumpWidget(BlocProvider(
  create: (context) => WeatherBloc(WeatherRepo()),
  child: const MaterialApp(
  home: CounterHomePage(),
  ),
 ));

 expect(find.text('Weather Search'), findsOneWidget);
 });
} 

Wrap your widget with MaterialApp in test environment.在测试环境中使用MaterialApp包装您的小部件。

Replace this:替换这个:

await tester.pumpWidget(HomeScreen());

with:和:

await tester.pumpWidget(MaterialApp(home:HomeScreen()));

Replace this替换这个

void main() {
  runApp(const MyApp());
}

with this有了这个

void main() {
  runApp(
    const MaterialApp(
      home: MyApp(),
    ),
  );
}

Wrap your widget with MaterialApp() and pass new class to home attribute of MaterialApp() widget.MaterialApp()包裹您的小部件并将新类传递给MaterialApp()小部件的home属性。

class MyApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return MaterialApp( //use MaterialApp() widget like this
      home: Home() //create new widget class for this 'home' to 
                   // escape 'No MediaQuery widget found' error
    );
  }
}

Reference From: How to Solve 'No MediaQuery widget found' Error in Flutter参考来源: 如何解决 Flutter 中“未找到 MediaQuery 小部件”错误

Wrap your main mathod from this从这里包装你的主要方法

 void main() {
  runApp( YourScreen(),
     );
}

to

    void main() {
  runApp(
    MaterialApp(
      home: LoginScreen(),
    ),
  );
}

My full code我的完整代码

    void main() {
  runApp(
    MaterialApp(
      home: LoginScreen(),
    ),
  );
}

class LoginScreen extends StatefulWidget {
  @override
  _LoginScreen createState() => _LoginScreen();
}

class _LoginScreen extends State<LoginScreen> {

  @override
  void initState() {
    super.initState();
  }


  @override
  Widget build(BuildContext context) {
      return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(
            "Salezrobot",
            style: TextStyle(
              fontSize: 24,
              color: Colors.white,
              fontWeight: FontWeight.bold,
              fontFamily: 'HelveticaNeue',
            ),
          ),
        ),
        body: Text(""),
      ),
    );
  }
}

I have tried this inFlutter 2.10.4我试过这个 inFlutter 2.10.4

Widget createWidgetForTesting({required Widget child}) {
    return MediaQuery(
          data: const MediaQueryData(),
          child:MaterialApp(home: Scaffold(body: child)));
}
testWidgets('Test todo title and description', (WidgetTester tester) async {
      final todo = MockData.mockTodosData[0];
      await tester
          .pumpWidget(createWidgetForTesting(child: TodoWidget(item: todo)));
      final titleFinder = find.text(todo.title);
      final descFinder = find.text(todo.desc);
      expect(titleFinder, findsOneWidget);
      expect(descFinder, findsOneWidget);
    });

It works fine..它工作正常..

In my case I recommend this starting scheme就我而言,我推荐这个起始方案

import 'package:flutter/material.dart';
import 'package:url_strategy/url_strategy.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

//void main() => runApp(HomePage());
void main() {
  //Initialize ScreenUtil
  ScreenUtil.ensureScreenSize();
  setPathUrlStrategy();
  runApp(HomePage());
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  MyApp createState() => MyApp();
}

class MyApp extends State<HomePage> {
  @override
  void initState() {
    super.initState();
  }

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    MaterialApp materialApp = MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'TITLE APP',
      theme: ThemeData(
        primaryColor: kPrimaryColor,
        scaffoldBackgroundColor: Color.fromARGB(255, 0, 0, 0),
      ),
      home: InicioScreen(
        context: context,
      ),
      onGenerateRoute: (RouteSettings settings) {
        switch (settings.name) {
          case '/':
            return MaterialPageRoute(
                builder: (context) => Screen1(context: this.context));
            break;
          case '/cartelera':
            return MaterialPageRoute(
              builder: (context) => Screen2(context: this.context),
            );
            break;
        }
      },
    );

    return MediaQuery(
        data: new MediaQueryData(),
        child: LayoutBuilder(
          builder: (context, constraints) {
            return OrientationBuilder(
              builder: (context, orientation) {
                ScreenUtil.init(context);
                // SizerUtil().init(constraints, orientation);
                return new MaterialApp(home: materialApp);
              },
            );
          },
        ));
  }

  @override
  void dispose() {
    super.dispose();
  }
}

Just wrap Scaffold inside MaterialApp, that worked只需将 Scaffold 包裹在 MaterialApp 中,就可以了

yo tuve el mismo error y lo solucione asi yo tuve el mismo error y lo solucione asi

asi me marcaba error asi me marcaba 错误

void main() => runApp(MainPage());

envolvi en un material app envolvi en un material 应用程序

void main() => runApp(MaterialApp(home: MainPage()));

reemplaza nomas estimado saludos reeplaza nomas estimado saludos

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 未找到 MediaQuery 小部件祖先 - No MediaQuery widget ancestor found Flutter,未找到 MediaQuery 小部件祖先 - Flutter, No MediaQuery widget ancestor found Flutter:未找到 MediaQuery 小部件祖先 - Flutter: No MediaQuery widget ancestor found 无法 flutter 中的 select 文本,未找到 MediaQuery 小部件 - Unable to select text in flutter, No MediaQuery widget found rawscrollbar 的“未找到 MediaQuery 小部件祖先”是什么 - What is " No MediaQuery widget ancestor found" for rawscrollbar 未处理的异常:未找到 MediaQuery 小部件 - 颤动 ShowModalButtonSheet - Unhandled Exception: No MediaQuery widget found - flutter ShowModalButtonSheet Flutter:EasyLoading library No MediaQuery widget found 错误 - Flutter: EasyLoading library No MediaQuery widget found error Flutter 小部件测试:未应用 MediaQuery 设置填充 - Flutter widget test: setting padding with MediaQuery not applied 手势引起的异常 未找到 MediaQuery 小部件。 MyHomePage 小部件需要 MediaQuery 小部件祖先 - EXCEPTION CAUGHT BY GESTURE No MediaQuery widget found. MyHomePage widgets require a MediaQuery widget ancestor 未找到 MediaQuery 小部件祖先。 Scaffold 小部件需要 MediaQuery 小部件祖先 - No MediaQuery widget ancestor found. Scaffold widgets require a MediaQuery widget ancestor
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM