Description
Steps to Reproduce
Friendly cc @blasten 😄
- Use the commits after this fix: Remove glitch when displaying platform views engine#30724
- Use the stable 2.10.3 engine version, or use the github.com/flutter/flutter master branch commit(a1a305c), it's exactly before "Roll Engine" commit before the new android platform view architecture merged into main engine branch(the new arch is ok without bug😄): flutter/engine@5f0271a:
- Use the demo Dart code below:
- Click any of the button in the list, navigate to webview and go back, you can see one frame glitch (about 30% probability), which position is around the clicked button's gray background.
Screenrecord
By camera:
IMG_0176.MOV
By adb screen record
:
video.mp4
Demo code:
CupertinoPageRoute Demo
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:webview_flutter/webview_flutter.dart';
void main() {
WebView.platform = SurfaceAndroidWebView();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(home: HomeScreen());
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
ListTile(
title: Text('Red'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 1'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 3'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, CupertinoPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
CircularProgressIndicator(
semanticsLabel: 'Linear progress indicator',
),
CircularProgressIndicator(
semanticsLabel: 'Linear progress indicator',
),
CircularProgressIndicator(
semanticsLabel: 'Linear progress indicator',
),
CircularProgressIndicator(
semanticsLabel: 'Linear progress indicator',
),
CircularProgressIndicator(
semanticsLabel: 'Linear progress indicator',
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
ListTile(
title: Text('Green 2'),
onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const ProducDetailPage();
}));
},
),
// CircularProgressIndicator(
// semanticsLabel: 'Linear progress indicator',
// ),
// SizedBox(
// height: 150,
// child: WebView(
// initialUrl: 'https://flutter.dev',
// ),
// ),
],
),
),
);
}
}
class ProducDetailPage extends StatelessWidget {
const ProducDetailPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Produc Detail'),
),
body: Column(children: const [
Padding(
padding: EdgeInsets.all(8.0),
child: SizedBox(
height: 150,
child: Placeholder(
color: Colors.red,
),
),
),
SizedBox(
height: 150,
child: WebView(
initialUrl: 'https://flutter.dev',
),
),
CircularProgressIndicator(
semanticsLabel: 'Linear progress indicator',
),
]));
}
}
class ProductDetailPage2 extends StatelessWidget {
const ProductDetailPage2({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Produc Detail'),
),
body: Column(children: const [
SizedBox(
height: 150,
child: Placeholder(
color: Colors.green,
),
),
]));
}
}
Here's my diagnosis:
Firstly, the commit https://github.com/flutter/engine/pull/30724/files#diff-87fa95e503a7a95a32aee115dded76fc6f8ec76fc29c2d2c071b413df0c3a096R265
introduced the isPaused
member, when isPaused is set to true, then the stopRenderingToSurface()
is not called any more:
https://github.com/flutter/engine/pull/30724/files#diff-d3ae30246a95a9e78606b57b860941c4d74d5dfba9ecc394f202c9a62002531bR242
Let's see there's a variable named isDisplayingFlutterUi
in FlutterRenderer.java :
It controls the logic in addIsDisplayingFlutterUiListener()
when isDisplayingFlutterUi
is true, the addIsDisplayingFlutterUiListener
will directly calls the listener
on current java call stack.
But in this issue's scenario, the code in RevertImageView()
:
- firstly calls
renderSurface.attachToRenderer(renderer);
but not setisDisplayingFlutterUi
to false; - secondly calls
addIsDisplayingFlutterUiListener
and runonDone.run()
directly on current stack.
It will
onDone.run()
will call finishFrame() and will setoverlayView.setVisibility(View.GONE);
:
But the gone of FlutterImageView
is too early! And I think the first new frame rendering on SurfaceView is not ready yet. The previous image of SurfaceView shows the click state (with a gray shadow) of the button..
I think the wrong stack is here:
After revert this commit, the original right stack is below: