Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:cupertino_ui/cupertino_ui.dart';

/// Flutter code sample for [CupertinoMagnifier].

void main() => runApp(const CupertinoMagnifierApp());

class CupertinoMagnifierApp extends StatelessWidget {
const CupertinoMagnifierApp({super.key});

@override
Widget build(BuildContext context) {
return const CupertinoApp(
theme: CupertinoThemeData(brightness: .light),
home: CupertinoMagnifierExample(),
);
}
}

class CupertinoMagnifierExample extends StatefulWidget {
const CupertinoMagnifierExample({super.key});

@override
State<CupertinoMagnifierExample> createState() =>
_CupertinoMagnifierExampleState();
}

class _CupertinoMagnifierExampleState extends State<CupertinoMagnifierExample> {
static const double magnifierRadius = 50.0;
Offset dragGesturePosition = .zero;

@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('CupertinoMagnifier Sample'),
),
child: Center(
child: Column(
mainAxisAlignment: .center,
children: <Widget>[
const Text('Drag on the logo!'),
RepaintBoundary(
child: Stack(
children: <Widget>[
GestureDetector(
onPanUpdate: (DragUpdateDetails details) {
setState(() {
dragGesturePosition = details.localPosition;
});
},
onPanDown: (DragDownDetails details) {
setState(() {
dragGesturePosition = details.localPosition;
});
},
child: const FlutterLogo(size: 200),
),
Positioned(
left: dragGesturePosition.dx - magnifierRadius,
top: dragGesturePosition.dy - magnifierRadius,
child: const CupertinoMagnifier(
magnificationScale: 1.5,
borderRadius: .all(Radius.circular(magnifierRadius)),
additionalFocalPointOffset: Offset(0, -magnifierRadius),
),
),
],
),
),
],
),
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:cupertino_ui/cupertino_ui.dart';

/// Flutter code sample for [CupertinoTextMagnifier].

void main() => runApp(const CupertinoTextMagnifierApp());

class CupertinoTextMagnifierApp extends StatelessWidget {
const CupertinoTextMagnifierApp({super.key});

@override
Widget build(BuildContext context) {
return const CupertinoApp(
theme: CupertinoThemeData(brightness: .light),
home: CupertinoTextMagnifierExampleApp(),
);
}
}

class CupertinoTextMagnifierExampleApp extends StatefulWidget {
const CupertinoTextMagnifierExampleApp({super.key});

@override
State<CupertinoTextMagnifierExampleApp> createState() =>
_CupertinoTextMagnifierExampleAppState();
}

class _CupertinoTextMagnifierExampleAppState
extends State<CupertinoTextMagnifierExampleApp> {
final MagnifierController _controller = MagnifierController();

@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: const CupertinoNavigationBar(
middle: Text('CupertinoTextMagnifier Sample'),
),
child: Padding(
padding: const .symmetric(horizontal: 48.0),
child: Center(
child: CupertinoTextField(
magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder:
(_, _, ValueNotifier<MagnifierInfo> magnifierInfo) {
return CupertinoTextMagnifier(
controller: _controller,
magnifierInfo: magnifierInfo,
);
},
),
controller: TextEditingController(text: 'Hello world!'),
),
),
),
);
}
}
Comment thread
Piinks marked this conversation as resolved.
107 changes: 107 additions & 0 deletions packages/cupertino_ui/example/lib/magnifier/text_magnifier.0.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/foundation.dart';
import 'package:material_ui/material_ui.dart';

void main() => runApp(const TextMagnifierExampleApp(text: 'Hello world!'));

class TextMagnifierExampleApp extends StatelessWidget {
const TextMagnifierExampleApp({
super.key,
this.textDirection = TextDirection.ltr,
required this.text,
});

final TextDirection textDirection;
final String text;

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Padding(
padding: const .symmetric(horizontal: 48.0),
child: Center(
child: TextField(
textDirection: textDirection,
// Create a custom magnifier configuration that
// this `TextField` will use to build a magnifier with.
magnifierConfiguration: TextMagnifierConfiguration(
magnifierBuilder:
(_, _, ValueNotifier<MagnifierInfo> magnifierInfo) =>
CustomMagnifier(magnifierInfo: magnifierInfo),
),
controller: TextEditingController(text: text),
),
),
),
),
);
}
}
Comment thread
Piinks marked this conversation as resolved.

class CustomMagnifier extends StatelessWidget {
const CustomMagnifier({super.key, required this.magnifierInfo});

static const Size magnifierSize = Size(200, 200);

// This magnifier will consume some text data and position itself
// based on the info in the magnifier.
final ValueNotifier<MagnifierInfo> magnifierInfo;

@override
Widget build(BuildContext context) {
// Use a value listenable builder because we want to rebuild
// every time the text selection info changes.
// `CustomMagnifier` could also be a `StatefulWidget` and call `setState`
// when `magnifierInfo` updates. This would be useful for more complex
// positioning cases.
return ValueListenableBuilder<MagnifierInfo>(
valueListenable: magnifierInfo,
builder: (BuildContext context, MagnifierInfo currentMagnifierInfo, _) {
// We want to position the magnifier at the global position of the gesture.
Offset magnifierPosition = currentMagnifierInfo.globalGesturePosition;

// You may use the `MagnifierInfo` however you'd like:
// In this case, we make sure the magnifier never goes out of the current line bounds.
magnifierPosition = Offset(
clampDouble(
magnifierPosition.dx,
currentMagnifierInfo.currentLineBoundaries.left,
currentMagnifierInfo.currentLineBoundaries.right,
),
clampDouble(
magnifierPosition.dy,
currentMagnifierInfo.currentLineBoundaries.top,
currentMagnifierInfo.currentLineBoundaries.bottom,
),
);

// Finally, align the magnifier to the bottom center. The initial anchor is
// the top left, so subtract bottom center alignment.
magnifierPosition -= Alignment.bottomCenter.alongSize(magnifierSize);

return Positioned(
left: magnifierPosition.dx,
top: magnifierPosition.dy,
child: RawMagnifier(
magnificationScale: 2,
// The focal point starts at the center of the magnifier.
// We probably want to point below the magnifier, so
// offset the focal point by half the magnifier height.
focalPointOffset: Offset(0, magnifierSize.height / 2),
// Decorate it however we'd like!
decoration: const MagnifierDecoration(
shape: StarBorder(
side: BorderSide(color: Colors.green, width: 2),
),
),
size: magnifierSize,
),
);
},
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:cupertino_ui/cupertino_ui.dart';
import 'package:cupertino_ui_examples/magnifier/cupertino_magnifier.0.dart'
as example;
import 'package:flutter_test/flutter_test.dart';

void main() {
testWidgets('CupertinoMagnifier must be visible', (
WidgetTester tester,
) async {
await tester.pumpWidget(const example.CupertinoMagnifierApp());

final Finder cupertinoMagnifierWidget = find.byType(CupertinoMagnifier);
expect(cupertinoMagnifierWidget, findsOneWidget);
});

testWidgets('CupertinoMagnifier is not using the default value', (
WidgetTester tester,
) async {
await tester.pumpWidget(const example.CupertinoMagnifierApp());
expect(
tester.widget(find.byType(CupertinoMagnifier)),
isA<CupertinoMagnifier>().having(
(CupertinoMagnifier t) => t.magnificationScale,
'magnificationScale',
1.5,
),
);
});

testWidgets('should update CupertinoMagnifier position on drag', (
WidgetTester tester,
) async {
await tester.pumpWidget(const example.CupertinoMagnifierApp());

Matcher isPositionedAt(Offset at) {
return isA<Positioned>().having(
(Positioned positioned) => Offset(positioned.left!, positioned.top!),
'magnifier position',
at,
);
}

// Make sure magnifier is present.
final Finder positionedWidget = find.byType(Positioned);
final Widget positionedWidgetInTree = tester.widget(positionedWidget.first);
final Positioned oldConcretePositioned =
positionedWidgetInTree as Positioned;
final Offset centerOfPositioned = tester.getCenter(positionedWidget.first);

// Drag the magnifier and confirm its new position is expected.
const Offset dragDistance = Offset(10, 10);
final Offset updatedPositioned = Offset(
oldConcretePositioned.left ?? 0.0 + 10.0,
oldConcretePositioned.top ?? 0.0 + 10.0,
);

await tester.dragFrom(centerOfPositioned, dragDistance);
await tester.pump();
expect(positionedWidgetInTree, isPositionedAt(updatedPositioned));
Comment thread
Piinks marked this conversation as resolved.
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2013 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:cupertino_ui/cupertino_ui.dart';
import 'package:cupertino_ui_examples/magnifier/cupertino_text_magnifier.0.dart'
as example;
import 'package:flutter_test/flutter_test.dart';

void main() {
testWidgets('CupertinoTextMagnifier must be visible after longPress', (
WidgetTester tester,
) async {
await tester.pumpWidget(const example.CupertinoTextMagnifierApp());

final Finder cupertinoTextFieldWidget = find.byType(CupertinoTextField);
await tester.longPress(cupertinoTextFieldWidget);

final Finder cupertinoTextMagnifierWidget = find.byType(
CupertinoTextMagnifier,
);
expect(cupertinoTextMagnifierWidget, findsOneWidget);
});
}
Loading
Loading