Skip to content

Commit 93a57c5

Browse files
feat: add nested scroll screen for Appium testing (#33)
1 parent fbcd000 commit 93a57c5

2 files changed

Lines changed: 187 additions & 0 deletions

File tree

demo-app/lib/screens/home_screen.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:appium_testing_app/screens/ui_elements_screen.dart';
99
import 'package:appium_testing_app/screens/vertical_swiping_screen.dart';
1010
import 'package:appium_testing_app/screens/web_view_screen.dart';
1111
import 'package:appium_testing_app/screens/wheel_picker_screen.dart';
12+
import 'package:appium_testing_app/screens/nested_scroll.dart';
1213
import 'package:flutter/material.dart';
1314

1415
import 'carousel_screen.dart';
@@ -66,6 +67,8 @@ class _HomeScreenState extends State<HomeScreen> {
6667
title: "Contact permission", subtitle: "Asks for contact permission with native popup"));
6768
featureModels.add(FeatureModel(
6869
title: "Image Picker", subtitle: "Mock Camera Image Picker"));
70+
featureModels.add(FeatureModel(
71+
title: "Nested Scroll", subtitle: "Nested elements - For scroll"));
6972
}
7073

7174
@override
@@ -159,6 +162,9 @@ class _HomeScreenState extends State<HomeScreen> {
159162
case 15:
160163
page = ImagePickerScreen(title: featureModels[index].title,);
161164
break;
165+
case 16:
166+
page = NestedScrollLayoutScreen(title: featureModels[index].title);
167+
break;
162168
default:
163169
page = NativeScreen(title: featureModels[index].title);
164170
break;
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import 'package:appium_testing_app/components/custom_app_bar.dart';
2+
import 'package:flutter/material.dart';
3+
// To use the Lucide icons like in the HTML, you would add this dependency to your pubspec.yaml:
4+
// lucide_flutter: ^1.309.0
5+
// For this example, I'll use standard Material Icons as a fallback.
6+
// import 'package:lucide_flutter/lucide_flutter.dart';
7+
8+
class NestedScrollLayoutScreen extends StatefulWidget {
9+
final String title;
10+
11+
const NestedScrollLayoutScreen({required this.title, super.key});
12+
13+
@override
14+
State<NestedScrollLayoutScreen> createState() => _NestedScrollLayoutScreenState();
15+
}
16+
17+
class _NestedScrollLayoutScreenState extends State<NestedScrollLayoutScreen> {
18+
@override
19+
Widget build(BuildContext context) {
20+
return Scaffold(
21+
backgroundColor: Colors.grey[100],
22+
appBar: CustomAppBarWidget(title: widget.title),
23+
body: SingleChildScrollView(
24+
key: const ValueKey('nested_scroll_main_view'),
25+
child: Padding(
26+
padding: const EdgeInsets.all(16.0),
27+
child: Column(
28+
crossAxisAlignment: CrossAxisAlignment.start,
29+
children: [
30+
// Section Header
31+
const Padding(
32+
padding: EdgeInsets.only(top: 16.0, bottom: 8.0),
33+
child: Text(
34+
'List of Elements',
35+
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
36+
),
37+
),
38+
// List of Elements
39+
ListView.separated(
40+
key: const ValueKey('nested_elements_list'),
41+
physics: const NeverScrollableScrollPhysics(), // Important for nested scrolling
42+
shrinkWrap: true,
43+
itemCount: 4,
44+
separatorBuilder: (context, index) => const SizedBox(height: 24),
45+
itemBuilder: (context, index) {
46+
// The index for the parent element starts from 1
47+
return _ParentElementCard(elementIndex: index + 1);
48+
},
49+
),
50+
],
51+
),
52+
),
53+
),
54+
);
55+
}
56+
}
57+
58+
// A widget representing a single "Parent Element" row in the list
59+
class _ParentElementCard extends StatelessWidget {
60+
final int elementIndex;
61+
62+
const _ParentElementCard({required this.elementIndex});
63+
64+
@override
65+
Widget build(BuildContext context) {
66+
return Row(
67+
crossAxisAlignment: CrossAxisAlignment.start,
68+
children: [
69+
// Left-hand side indicator
70+
Column(
71+
children: [
72+
Container(
73+
key: ValueKey('parent_indicator_$elementIndex'),
74+
padding: const EdgeInsets.all(8),
75+
decoration: BoxDecoration(
76+
color: Colors.grey[200],
77+
shape: BoxShape.circle,
78+
),
79+
child: Icon(Icons.inventory_2_outlined, color: Colors.grey[800], size: 20),
80+
),
81+
const SizedBox(height: 8),
82+
// The vertical line connecting elements
83+
Container(
84+
width: 2,
85+
height: 150, // Adjust height as needed
86+
color: Colors.grey[300],
87+
),
88+
],
89+
),
90+
const SizedBox(width: 16),
91+
// Main content card
92+
Expanded(
93+
child: Card(
94+
key: ValueKey('parent_card_$elementIndex'),
95+
elevation: 2,
96+
margin: const EdgeInsets.all(0),
97+
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
98+
child: Padding(
99+
padding: const EdgeInsets.all(16.0),
100+
child: Column(
101+
crossAxisAlignment: CrossAxisAlignment.start,
102+
children: [
103+
Semantics(
104+
label: "parent_element_title_$elementIndex",
105+
child: Text(
106+
'Parent Element $elementIndex',
107+
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
108+
),
109+
),
110+
const SizedBox(height: 4),
111+
Text(
112+
'This is a descriptive text for Parent Element $elementIndex.',
113+
style: TextStyle(color: Colors.grey[600], fontSize: 14),
114+
),
115+
const Divider(height: 24),
116+
const Text(
117+
'Child Elements:',
118+
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 14),
119+
),
120+
const SizedBox(height: 8),
121+
// Child elements list
122+
_ChildElementRow(parentIndex: elementIndex, childIndex: 1),
123+
const SizedBox(height: 8),
124+
_ChildElementRow(parentIndex: elementIndex, childIndex: 2),
125+
const SizedBox(height: 8),
126+
_ChildElementRow(parentIndex: elementIndex, childIndex: 3),
127+
],
128+
),
129+
),
130+
),
131+
),
132+
],
133+
);
134+
}
135+
}
136+
137+
// A widget for a single "Child Element" row inside the card
138+
class _ChildElementRow extends StatelessWidget {
139+
final int parentIndex;
140+
final int childIndex;
141+
142+
const _ChildElementRow({
143+
required this.parentIndex,
144+
required this.childIndex,
145+
});
146+
147+
@override
148+
Widget build(BuildContext context) {
149+
return Container(
150+
key: ValueKey('child_element_${parentIndex}_$childIndex'),
151+
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
152+
decoration: BoxDecoration(
153+
color: Colors.grey[100],
154+
borderRadius: BorderRadius.circular(8),
155+
),
156+
child: Row(
157+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
158+
children: [
159+
Semantics(
160+
label: "child_element_name_${parentIndex}_$childIndex",
161+
child: Text(
162+
'Child $childIndex',
163+
style: const TextStyle(fontSize: 14),
164+
),
165+
),
166+
Semantics(
167+
label: "child_element_id_${parentIndex}_$childIndex",
168+
child: Text(
169+
'ID: P$parentIndex-C$childIndex',
170+
style: TextStyle(
171+
fontFamily: 'monospace',
172+
fontSize: 12,
173+
color: Colors.grey[600],
174+
),
175+
),
176+
),
177+
],
178+
),
179+
);
180+
}
181+
}

0 commit comments

Comments
 (0)