Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class _MyAppState extends State<MyApp> {
selectAll: true,
share: true,
),
enableLineNumbers: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

showLineNumbers

),
),
const SizedBox(height: 24),
Expand All @@ -56,6 +57,7 @@ class _MyAppState extends State<MyApp> {
code: codeSnippet,
controller: controller,
showCursor: true,
enableLineNumbers: true,
),
),
],
Expand Down
84 changes: 58 additions & 26 deletions lib/src/presentation/code_edit_text.dart
Original file line number Diff line number Diff line change
@@ -1,35 +1,60 @@
import 'package:flutter/material.dart';
import 'package:kode_view/src/presentation/line_numbers_wrapper.dart';
import 'package:kode_view/src/presentation/syntax_highlighting_controller.dart';
import 'package:kode_view/src/presentation/text_selection_options.dart';

class CodeEditText extends StatefulWidget {
const CodeEditText({
required this.code,

// Core functionality
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even this is a nice segregation, this can lead to problems with linting and formatting, please order params by conceptual groups but remove the comments

this.controller,
this.maxLines,
this.options,
this.showCursor,
this.onTap,
this.language,
this.theme,
this.textStyle,
this.maxLines,

// Appearance
this.decoration,
this.enableLineNumbers = false,
this.lineNumberColor,
this.lineNumberBackgroundColor,

// Interaction
this.showCursor,
this.options,
this.onTap,

// Debugging
this.debug = false,

// Flutter widget key
super.key,
});

// Required
final String code;

// Core functionality
final SyntaxHighlightingController? controller;
final String? language;
final String? theme;
final TextStyle? textStyle;
final int? maxLines;

// Appearance
final InputDecoration? decoration;
final bool enableLineNumbers;
final Color? lineNumberColor;
final Color? lineNumberBackgroundColor;

// Interaction
final bool? showCursor;
final TextSelectionOptions? options;
final InputDecoration? decoration;
final GestureTapCallback? onTap;
final SyntaxHighlightingController? controller;
final bool debug;

final String? language;
final String? theme;
final TextStyle? textStyle;
// Debugging
final bool debug;

@override
State<CodeEditText> createState() => _CodeEditTextState();
Expand All @@ -42,8 +67,11 @@ class _CodeEditTextState extends State<CodeEditText> {
void initState() {
super.initState();
_controller = widget.controller ??
SyntaxHighlightingController(text: widget.code, debug: widget.debug,
)..addListener(() {
SyntaxHighlightingController(
text: widget.code,
debug: widget.debug,
)
..addListener(() {
_controller.updateSyntaxHighlighting(
code: _controller.text,
language: widget.language,
Expand All @@ -65,20 +93,24 @@ class _CodeEditTextState extends State<CodeEditText> {
return ValueListenableBuilder(
valueListenable: _controller.textSpansNotifier,
builder: (context, __, _) {
return TextField(
controller: _controller,
style: widget.textStyle,
onTap: widget.onTap,
minLines: 1,
maxLines: widget.maxLines,
contextMenuBuilder: widget.options != null
? (context, editableTextState) =>
widget.options!.toolbarOptions(context, editableTextState)
: null,
enableInteractiveSelection: widget.options != null,
showCursor: widget.showCursor ?? true,
scrollPhysics: const ClampingScrollPhysics(),
decoration: widget.decoration,
return LineNumbersWrapper(
enableLineNumbers: widget.enableLineNumbers,
linesNumber: _controller.text.split('\n').length,
child: TextField(
controller: _controller,
style: widget.textStyle,
onTap: widget.onTap,
minLines: 1,
maxLines: widget.maxLines,
contextMenuBuilder: widget.options != null
? (context, editableTextState) =>
widget.options!.toolbarOptions(context, editableTextState)
: null,
enableInteractiveSelection: widget.options != null,
showCursor: widget.showCursor ?? true,
scrollPhysics: const ClampingScrollPhysics(),
decoration: widget.decoration,
),
);
},
);
Expand Down
34 changes: 21 additions & 13 deletions lib/src/presentation/code_text_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:highlights_plugin/highlights_plugin.dart';
import 'package:kode_view/src/presentation/line_numbers_wrapper.dart';
import 'package:kode_view/src/presentation/styles/text_styles.dart';
import 'package:kode_view/src/presentation/text_selection_options.dart';
import 'package:kode_view/src/utils/extensions/collection_extensions.dart';
Expand All @@ -17,6 +18,7 @@ class CodeTextView extends StatelessWidget {
this.language,
this.theme,
this.textStyle,
this.enableLineNumbers = false,
super.key,
});

Expand All @@ -26,6 +28,7 @@ class CodeTextView extends StatelessWidget {
final bool? showCursor;
final TextSelectionOptions? options;
final GestureTapCallback? onTap;
final bool enableLineNumbers;

final String? language;
final String? theme;
Expand All @@ -39,6 +42,7 @@ class CodeTextView extends StatelessWidget {
this.language,
this.theme,
this.textStyle,
this.enableLineNumbers = false,
super.key,
}) : maxLines = 5;

Expand All @@ -50,19 +54,23 @@ class CodeTextView extends StatelessWidget {
initialData: const <TextSpan>[],
future: _highlights(maxLinesOrAll),
builder: (_, value) {
return SelectableText.rich(
TextSpan(children: value.requireData),
style: textStyle ?? TextStyles.code(code).style!,
minLines: 1,
maxLines: maxLinesOrAll,
onTap: () {},
contextMenuBuilder: options != null
? (context, editableTextState) =>
options!.toolbarOptions(context, editableTextState)
: null,
enableInteractiveSelection: options != null,
showCursor: showCursor ?? false,
scrollPhysics: const ClampingScrollPhysics(),
return LineNumbersWrapper(
enableLineNumbers: enableLineNumbers,
linesNumber: maxLinesOrAll,
child: SelectableText.rich(
TextSpan(children: value.requireData),
style: textStyle ?? TextStyles.code(code).style!,
minLines: 1,
maxLines: maxLinesOrAll,
onTap: () {},
contextMenuBuilder: options != null
? (context, editableTextState) =>
options!.toolbarOptions(context, editableTextState)
: null,
enableInteractiveSelection: options != null,
showCursor: showCursor ?? false,
scrollPhysics: const ClampingScrollPhysics(),
),
);
},
);
Expand Down
54 changes: 54 additions & 0 deletions lib/src/presentation/line_numbers_wrapper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
import 'package:kode_view/src/presentation/styles/text_styles.dart';

class LineNumbersWrapper extends StatelessWidget {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Brilliant idea to use wrapper for that!

const LineNumbersWrapper({
required this.enableLineNumbers,
required this.linesNumber,
required this.child,
this.textStyle,
this.lineNumberBackgroundColor,
this.lineNumberColor,
super.key,
});
final bool enableLineNumbers;
final int linesNumber;
final Color? lineNumberBackgroundColor;
final Color? lineNumberColor;
final TextStyle? textStyle;
final Widget child;
@override
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space

Widget build(BuildContext context) {
return Row(
children: [
if (enableLineNumbers) ...[
Container(
color: lineNumberBackgroundColor,
width: 40,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have default spacings, but also enable customization. What about line 11111? This will overflow?

child: ListView.builder(
shrinkWrap: true,
itemCount: linesNumber,
itemBuilder: (context, index) {
return Text(
'${index + 1}',
textAlign: TextAlign.right,
style: TextStyle(
color: lineNumberColor ?? Colors.black54,
fontSize: textStyle?.fontSize ??
const TextStyles.code('').style!.fontSize,
),
);
},
),
),
const SizedBox(
width: 4,
),
],
Expanded(
child: child,
),
],
);
}
}