Skip to content

Commit

Permalink
[flutter_markdown] Ensure customize nested bullet list style. (#6384)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kurogoma4D committed Apr 19, 2024
1 parent bb82cb7 commit 5d15437
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 6 deletions.
6 changes: 6 additions & 0 deletions packages/flutter_markdown/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.7.0

* **BREAKING CHANGES**:
* Replaces parameters at `bulletBuilder` with `MarkdownBulletParameters`.
* Introduces a new parameter `nestLevel` that exposes the bullet item's nesting level.

## 0.6.23

* Gracefully handle image dimension parsing failures.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// TODO(goderbauer): Restructure the examples to avoid this ignore, https://github.com/flutter/flutter/issues/110208.
// ignore_for_file: avoid_implementing_value_types

import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import '../shared/markdown_demo_widget.dart';

// ignore_for_file: public_member_api_docs

const String _markdownData = '''
# Custom Ordered List Demo
## Unordered List
- first
- second
- first
- first
- second
- first
- second
## Ordered List
1. first
2. second
1. first
1. first
2. second
1. first
1. second
''';

const String _notes = '''
# Custom Bullet List Demo
---
## Overview
This is the custom bullet list demo. This demo shows how to customize the bullet list style.
This demo example is being preserved for reference purposes.
''';

class CustomBulletListDemo extends StatelessWidget
implements MarkdownDemoWidget {
const CustomBulletListDemo({super.key});

static const String _title = 'Custom Bullet List Demo';

@override
String get title => CustomBulletListDemo._title;

@override
String get description => 'Shows how to customize the bullet list style.';

@override
Future<String> get data => Future<String>.value(_markdownData);

@override
Future<String> get notes => Future<String>.value(_notes);

@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Markdown(
data: _markdownData,
bulletBuilder: (MarkdownBulletParameters parameters) => FittedBox(
fit: BoxFit.scaleDown,
child: switch (parameters.style) {
BulletStyle.unorderedList => const Text('・'),
BulletStyle.orderedList =>
Text('${parameters.nestLevel}-${parameters.index + 1}.'),
},
),
),
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:flutter/material.dart';
import '../demos/basic_markdown_demo.dart';
import '../demos/centered_header_demo.dart';
import '../demos/custom_bullet_list_demo.dart';
import '../demos/extended_emoji_demo.dart';
import '../demos/markdown_body_shrink_wrap_demo.dart';
import '../demos/minimal_markdown_demo.dart';
Expand All @@ -30,6 +31,7 @@ class HomeScreen extends StatelessWidget {
OriginalMarkdownDemo(),
const CenteredHeaderDemo(),
const MarkdownBodyShrinkWrapDemo(),
const CustomBulletListDemo(),
];

@override
Expand Down
11 changes: 9 additions & 2 deletions packages/flutter_markdown/lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -611,8 +611,15 @@ class MarkdownBuilder implements md.NodeVisitor {
if (bulletBuilder != null) {
return Padding(
padding: styleSheet.listBulletPadding!,
child: bulletBuilder!(index,
isUnordered ? BulletStyle.unorderedList : BulletStyle.orderedList),
child: bulletBuilder!(
MarkdownBulletParameters(
index: index,
style: isUnordered
? BulletStyle.unorderedList
: BulletStyle.orderedList,
nestLevel: _listIndents.length - 1,
),
),
);
}

Expand Down
25 changes: 24 additions & 1 deletion packages/flutter_markdown/lib/src/widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,30 @@ typedef MarkdownCheckboxBuilder = Widget Function(bool value);
/// Signature for custom bullet widget.
///
/// Used by [MarkdownWidget.bulletBuilder]
typedef MarkdownBulletBuilder = Widget Function(int index, BulletStyle style);
typedef MarkdownBulletBuilder = Widget Function(
MarkdownBulletParameters parameters,
);

/// An parameters of [MarkdownBulletBuilder].
///
/// Used by [MarkdownWidget.bulletBuilder]
class MarkdownBulletParameters {
/// Creates a new instance of [MarkdownBulletParameters].
const MarkdownBulletParameters({
required this.index,
required this.style,
required this.nestLevel,
});

/// The index of the bullet on that nesting level.
final int index;

/// The style of the bullet.
final BulletStyle style;

/// The nest level of the bullet.
final int nestLevel;
}

/// Enumeration sent to the user when calling [MarkdownBulletBuilder]
///
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_markdown/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: A Markdown renderer for Flutter. Create rich text output,
formatted with simple Markdown tags.
repository: https://github.com/flutter/packages/tree/main/packages/flutter_markdown
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_markdown%22
version: 0.6.23
version: 0.7.0

environment:
sdk: ^3.3.0
Expand Down
62 changes: 60 additions & 2 deletions packages/flutter_markdown/test/list_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,35 @@ void defineTests() {
]);
},
);

testWidgets('custom bullet builder', (WidgetTester tester) async {
const String data =
'* Item 1\n * Item 2\n * Item 3\n * Item 4\n* Item 5';
Widget builder(MarkdownBulletParameters parameters) => Text(
'${parameters.index} ${parameters.style == BulletStyle.orderedList ? 'ordered' : 'unordered'} ${parameters.nestLevel}',
);

await tester.pumpWidget(
boilerplate(
Markdown(data: data, bulletBuilder: builder),
),
);

final Iterable<Widget> widgets = tester.allWidgets;

expectTextStrings(widgets, <String>[
'0 unordered 0',
'Item 1',
'0 unordered 1',
'Item 2',
'0 unordered 2',
'Item 3',
'1 unordered 1',
'Item 4',
'1 unordered 0',
'Item 5',
]);
});
});

group('Ordered List', () {
Expand Down Expand Up @@ -135,6 +164,35 @@ void defineTests() {
final Iterable<Widget> widgets = tester.allWidgets;
expectTextStrings(widgets, <String>['1.', 'one', 'two']);
});

testWidgets('custom bullet builder', (WidgetTester tester) async {
const String data =
'1. Item 1\n 1. Item 2\n 1. Item 3\n 1. Item 4\n1. Item 5';
Widget builder(MarkdownBulletParameters parameters) => Text(
'${parameters.index} ${parameters.style == BulletStyle.orderedList ? 'ordered' : 'unordered'} ${parameters.nestLevel}',
);

await tester.pumpWidget(
boilerplate(
Markdown(data: data, bulletBuilder: builder),
),
);

final Iterable<Widget> widgets = tester.allWidgets;

expectTextStrings(widgets, <String>[
'0 ordered 0',
'Item 1',
'0 ordered 1',
'Item 2',
'0 ordered 2',
'Item 3',
'1 ordered 1',
'Item 4',
'1 ordered 0',
'Item 5',
]);
});
});

group('Task List', () {
Expand All @@ -161,8 +219,8 @@ void defineTests() {

testWidgets('custom bullet builder', (WidgetTester tester) async {
const String data = '* Item 1\n* Item 2\n1) Item 3\n2) Item 4';
Widget builder(int index, BulletStyle style) => Text(
'$index ${style == BulletStyle.orderedList ? 'ordered' : 'unordered'}');
Widget builder(MarkdownBulletParameters parameters) => Text(
'${parameters.index} ${parameters.style == BulletStyle.orderedList ? 'ordered' : 'unordered'}');

await tester.pumpWidget(
boilerplate(
Expand Down

0 comments on commit 5d15437

Please sign in to comment.