Flutter文本计算插件text_calculator的使用

Flutter文本计算插件text_calculator的使用

你可以通过这个插件在渲染页面之前计算所需的页数。此外,它还提供了每个单词的坐标和宽高信息。

其他信息

查看示例目录中的Flutter应用以了解所有可用功能。

示例代码

以下是一个完整的示例代码,展示了如何使用text_calculator插件。

import 'package:flutter/material.dart';
import 'package:text_calculator/text_calculator.dart';

void main() {
  runApp(const MyApp());
}

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Text Calculator Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      debugShowCheckedModeBanner: false,
      home: const MyHomePage(title: 'Text Calculator Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  [@override](/user/override)
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final textcalculator = TextCalculator();
  List<Widget> pagedTextWidget = [];

  final double leftMargin = 12;
  final double leftPadding = 12;
  final double rightMargin = 12;
  final double rightPadding = 12;
  final double topMargin = 20;
  final double topPadding = 12;
  final double bottomMargin = 20;
  final double bottomPadding = 12;
  final double fontSize = 18;
  late int currentParagraphIndex = 0;
  late int currentPageIndex = 0;
  final globalKey = GlobalKey();

  [@override](/user/override)
  void initState() {
    super.initState();
    initTextCalculator();
  }

  Future<void> initTextCalculator() async {
    await textcalculator.setOriginText(
        originText:
            'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. At elementum eu facilisis sed odio morbi quis commodo. \nCras tincidunt lobortis feugiat vivamus at augue eget. Lacus luctus accumsan tortor posuere ac. Sit amet consectetur adipiscing elit duis. Rutrum quisque non tellus orci ac auctor augue. Nascetur ridiculus mus mauris vitae ultricies leo integer malesuada. Augue lacus viverra vitae congue eu consequat ac felis donec. Velit ut tortor pretium viverra suspendisse potenti. Ac turpis egestas maecenas pharetra convallis posuere morbi. Quis viverra nibh cras pulvinar mattis nunc sed. Ullamcorper malesuada proin libero nunc consequat interdum varius sit amet. Posuere ac ut consequat semper. Lectus magna fringilla urna porttitor rhoncus dolor. Vulputate eu scelerisque felis imperdiet proin fermentum leo vel. Odio ut enim blandit volutpat maecenas volutpat blandit.\n###Pellentesque nec nam aliquam sem. Quam pellentesque nec nam aliquam sem et tortor. Eget lorem dolor sed viverra ipsum nunc aliquet bibendum enim. Porta lorem mollis aliquam ut porttitor. Scelerisque eu ultrices vitae auctor eu augue ut lectus. Semper eget duis at tellus at urna condimentum mattis pellentesque. Vitae turpis massa sed elementum tempus egestas sed. Rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt. Sapien et ligula ullamcorper malesuada proin. Ac placerat vestibulum lectus mauris ultrices eros in cursus.\n###Et ligula ullamcorper malesuada proin libero nunc consequat. Libero volutpat sed cras ornare arcu dui. Non consectetur a erat nam at. Dignissim suspendisse in est ante in nibh mauris cursus. Sit amet massa vitae tortor condimentum lacinia quis vel. Amet aliquam id diam maecenas ultricies mi. Dui nunc mattis enim ut tellus elementum sagittis vitae. Facilisi morbi tempus iaculis urna id volutpat lacus. Dignissim cras tincidunt lobortis feugiat vivamus at augue eget arcu. Lorem sed risus ultricies tristique nulla aliquet enim tortor at. At in tellus integer feugiat scelerisque varius morbi. Dui ut ornare lectus sit amet est. Massa tincidunt nunc pulvinar sapien et ligula ullamcorper malesuada proin. Enim praesent elementum facilisis leo vel fringilla est. Elit ut aliquam purus sit amet luctus venenatis lectus magna. Eros donec ac odio tempor. Aenean et tortor at risus.\n###Cras pulvinar mattis nunc sed blandit libero volutpat. Tortor aliquam nulla facilisi cras fermentum. Nisi vitae suscipit tellus mauris a diam maecenas. Sit amet nulla facilisi morbi tempus. Fermentum dui faucibus in ornare quam viverra orci sagittis eu. Volutpat blandit aliquam etiam erat. Enim sed faucibus turpis in eu mi. Ornare quam viverra orci sagittis. In metus vulputate eu scelerisque felis. Risus pretium quam vulputate dignissim suspendisse in est ante. Id consectetur purus ut faucibus. Facilisis mauris sit amet massa vitae. Dictum varius duis at consectetur lorem donec massa. Accumsan in nisl nisi scelerisque eu ultrices vitae auctor. Volutpat blandit aliquam etiam erat velit scelerisque in. Id interdum velit laoreet id donec ultrices tincidunt arcu non.\n###Diam sollicitudin tempor id eu nisl nunc mi ipsum faucibus. Ultrices vitae auctor eu augue ut. Faucibus scelerisque eleifend donec pretium vulputate sapien nec sagittis. Faucibus interdum posuere lorem ipsum dolor sit. Ultrices mi tempus imperdiet nulla malesuada pellentesque elit. Pellentesque elit ullamcorper dignissim cras tincidunt. Purus gravida quis blandit turpis. Mauris in aliquam sem fringilla ut. Massa ultricies mi quis hendrerit dolor. Quam viverra orci sagittis eu volutpat odio facilisis. Faucibus turpis in eu mi bibendum neque. Facilisis gravida neque convallis a cras. Mauris sit amet massa vitae tortor condimentum. Enim nec dui nunc mattis enim ut tellus elementum. Sed felis eget velit aliquet sagittis id consectetur purus ut. Eget egestas purus viverra accumsan in nisl nisi scelerisque. Ac odio tempor orci dapibus ultrices in iaculis. Non quam lacus suspendisse faucibus interdum posuere lorem ipsum dolor. Amet aliquam id diam maecenas ultricies mi eget mauris pharetra. Dictum non consectetur a erat nam at lectus.');
    await textcalculator.setFontSize(fontSize: 18);
  }

  Future<void> updateTextCalculator(BuildContext context) async {
    await textcalculator.setHeight(
        height: MediaQuery.of(context).size.height * 0.6 -
            (topPadding + bottomPadding));
    await textcalculator.setWidth(
        width: MediaQuery.of(context).size.width -
            (leftMargin + leftPadding + rightMargin + rightPadding));
    await textcalculator.generateTotalTextList();
    await textcalculator.generateTotalGlobalKeyList();
    await textcalculator.generateTotalTextWidgetList();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Scaffold(
          appBar: AppBar(
            backgroundColor: Theme.of(context).colorScheme.inversePrimary,
            title: Text(widget.title),
          ),
          body: Stack(
            children: [
              Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  LayoutBuilder(builder: (context, constraints) {
                    return FutureBuilder(
                        future: updateTextCalculator(context),
                        builder: (context, snapshot) {
                          if (snapshot.connectionState == ConnectionState.done) {
                            try {
                              pagedTextWidget =
                                  textcalculator.totalTextWidgetList[currentParagraphIndex][currentPageIndex];
                            } catch (e) {
                              if (e is RangeError) {
                                setState(() {
                                  currentPageIndex = 0;
                                });
                              }
                            }

                            return Column(
                              children: [
                                Text(
                                  'currentParagraphIndex : $currentParagraphIndex',
                                  style: const TextStyle(fontSize: 10),
                                ),
                                Text('currentPageIndex : $currentPageIndex',
                                    style: const TextStyle(fontSize: 10)),
                                Text(
                                    'Paragraph : $currentParagraphIndex/${textcalculator.totalTextWidgetList.length - 1}',
                                    style: const TextStyle(fontSize: 10)),
                                Text(
                                    'Page : $currentPageIndex/${textcalculator.totalTextWidgetList[currentParagraphIndex].length - 1}',
                                    style: const TextStyle(fontSize: 10)),
                                Container(
                                    key: globalKey,
                                    decoration: BoxDecoration(
                                      borderRadius: BorderRadius.circular(20),
                                      color: Colors.amber,
                                    ),
                                    height: MediaQuery.of(context).size.height * 0.6,
                                    margin: EdgeInsets.fromLTRB(leftMargin,
                                        topMargin, rightMargin, bottomMargin),
                                    padding: EdgeInsets.fromLTRB(
                                        leftPadding,
                                        topPadding,
                                        rightPadding,
                                        bottomPadding),
                                    child: Align(
                                      alignment: Alignment.centerLeft,
                                      child: Column(
                                        children: List.from(pagedTextWidget),
                                      ),
                                    )),
                              ],
                            );
                          } else if (snapshot.connectionState == ConnectionState.values) {
                            return Column(
                              children: [
                                Text(
                                    'currentParagraphIndex : $currentParagraphIndex',
                                    style: const TextStyle(fontSize: 10)),
                                Text('currentPageIndex : $currentPageIndex',
                                    style: const TextStyle(fontSize: 10)),
                                Text(
                                    'Paragraph : $currentParagraphIndex/${textcalculator.totalTextWidgetList.length - 1}',
                                    style: const TextStyle(fontSize: 10)),
                                Text(
                                    'Page : $currentPageIndex/${textcalculator.totalTextWidgetList[currentParagraphIndex].length - 1}',
                                    style: const TextStyle(fontSize: 10)),
                                Container(
                                    decoration: BoxDecoration(
                                      borderRadius: BorderRadius.circular(20),
                                      color: Colors.amber,
                                    ),
                                    height: MediaQuery.of(context).size.height * 0.6,
                                    margin: EdgeInsets.fromLTRB(leftMargin,
                                        topMargin, rightMargin, bottomMargin),
                                    padding: EdgeInsets.fromLTRB(
                                        leftPadding,
                                        topPadding,
                                        rightPadding,
                                        bottomPadding),
                                    child: Align(
                                      alignment: Alignment.centerLeft,
                                      child: Column(
                                        children: List.from(pagedTextWidget),
                                      ),
                                    )),
                              ],
                            );
                          } else {
                            return const SizedBox.shrink();
                          }
                        });
                  }),
                  Row(
                    children: [
                      ElevatedButton(
                        onPressed: () async {
                          if (currentPageIndex == textcalculator.totalTextWidgetList[currentParagraphIndex].length - 1 && currentParagraphIndex == textcalculator.totalTextWidgetList.length - 1) {
                            print("Last Page");
                          } else if (currentPageIndex < textcalculator.totalTextWidgetList[currentParagraphIndex].length - 1) {
                            setState(() {
                              currentPageIndex += 1;
                            });
                          } else {
                            setState(() {
                              currentPageIndex = 0;
                              currentParagraphIndex += 1;
                            });
                          }
                        },
                        child: const Text(
                          'Next',
                          style: TextStyle(color: Colors.deepOrange),
                        ),
                      ),
                      ElevatedButton(
                        onPressed: () {
                          setState(() {
                            currentPageIndex = 0;
                            currentParagraphIndex = 0;
                          });
                        },
                        child: const Text(
                          'Reset',
                          style: TextStyle(color: Colors.deepOrange),
                        ),
                      ),
                      ElevatedButton(
                        onPressed: () async {
                          try {
                            final renderBox = globalKey.currentContext?.findRenderObject() as RenderBox?;
                            final position = renderBox?.localToGlobal(Offset.zero);
                            print(position.toString());
                            await textcalculator.setOrigin(
                                offset: Offset(
                                    position!.dx + leftMargin + leftPadding,
                                    position.dy + topMargin + topPadding + fontSize));
                            await textcalculator.generateTotalTextList();
                          } catch (e, s) {
                            print(e.toString());
                            print(s.toString());
                          }
                        },
                        child: const Text(
                          'Calculate First Offset',
                          style: TextStyle(color: Colors.deepOrange),
                        ),
                      )
                    ],
                  ),
                ],
              ),
            ],
          ),
        ),
        if (textcalculator.totalWordAoiList.isNotEmpty)
          CustomPaint(
              painter: DotPainter(textcalculator.totalWordAoiList[currentParagraphIndex][currentPageIndex]
                  .map((wordAoi) => Offset(wordAoi.x!, wordAoi.y!))
                  .toList()))
      ],
    );
  }
}

class VerticalLineAtX extends StatelessWidget {
  final double x;

  const VerticalLineAtX({super.key, required this.x});

  [@override](/user/override)
  Widget build(BuildContext context) {
    final screenHeight = MediaQuery.of(context).size.height;

    return Stack(
      children: [
        Positioned(
          left: x,
          child: Container(
            width: 1,
            height: screenHeight,
            color: Colors.red,
          ),
        ),
      ],
    );
  }
}

class HorizontalLineAtY extends StatelessWidget {
  final double y;

  const HorizontalLineAtY({super.key, required this.y});

  [@override](/user/override)
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;

    return Stack(
      children: [
        Positioned(
          top: y,
          child: Container(
            width: screenWidth,
            height: 1,
            color: Colors.blue,
          ),
        ),
      ],
    );
  }
}

class DotPainter extends CustomPainter {
  final List<Offset> points;

  DotPainter(this.points);

  [@override](/user/override)
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.blue
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 5.0;

    for (var point in points) {
      canvas.drawCircle(point, 5.0, paint);
    }
  }

  [@override](/user/override)
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return false;
  }
}

更多关于Flutter文本计算插件text_calculator的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter文本计算插件text_calculator的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用text_calculator插件的示例代码。这个插件通常用于计算文本的高度和宽度,以便在UI布局中进行适当的调整。不过,需要注意的是,text_calculator并不是Flutter官方或广泛使用的插件之一,因此这里的示例代码将基于一个假设的API接口。

在实际项目中,如果text_calculator插件不存在或API有所不同,你可能需要查找并使用一个类似功能的插件,如flutter_text_renderer或手动计算文本布局(使用TextPainter类)。但为了本次示例,我们将假设text_calculator插件提供了类似的功能。

首先,你需要在pubspec.yaml文件中添加依赖项(假设text_calculator插件存在):

dependencies:
  flutter:
    sdk: flutter
  text_calculator: ^x.y.z  # 替换为实际版本号

然后运行flutter pub get来安装依赖。

接下来,在你的Dart文件中使用text_calculator插件。以下是一个示例代码,展示如何计算文本的高度和宽度:

import 'package:flutter/material.dart';
import 'package:text_calculator/text_calculator.dart';  // 假设的包导入路径

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Text Calculator Example'),
        ),
        body: Center(
          child: TextCalculatorExample(),
        ),
      ),
    );
  }
}

class TextCalculatorExample extends StatefulWidget {
  @override
  _TextCalculatorExampleState createState() => _TextCalculatorExampleState();
}

class _TextCalculatorExampleState extends State<TextCalculatorExample> {
  double? textWidth;
  double? textHeight;

  @override
  void initState() {
    super.initState();
    _calculateTextDimensions();
  }

  void _calculateTextDimensions() {
    // 假设TextCalculator有一个静态方法calculateDimensions
    final TextStyle textStyle = TextStyle(fontSize: 20, color: Colors.black);
    final String text = "Hello, this is a sample text for demonstration.";

    // 调用插件的方法来计算文本尺寸(这里的方法名是假设的)
    TextDimensions dimensions = TextCalculator.calculateDimensions(
      text: text,
      style: textStyle,
      constraints: BoxConstraints(maxWidth: double.infinity),
    );

    setState(() {
      textWidth = dimensions.width;
      textHeight = dimensions.height;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text(
          "Text Width: $textWidth\nText Height: $textHeight",
          style: TextStyle(fontSize: 18),
        ),
        SizedBox(height: 20),
        Container(
          decoration: BoxDecoration(
            border: Border.all(color: Colors.black),
          ),
          constraints: BoxConstraints(
            width: textWidth ?? 100,  // 默认值以防textWidth为null
            height: textHeight ?? 50, // 默认值以防textHeight为null
          ),
          child: Center(
            child: Text(
              "Hello, this is a sample text for demonstration.",
              style: TextStyle(fontSize: 20, color: Colors.black),
            ),
          ),
        ),
      ],
    );
  }
}

// 假设TextCalculator插件返回的TextDimensions类
class TextDimensions {
  final double width;
  final double height;

  TextDimensions({required this.width, required this.height});
}

注意

  1. 上面的代码假设TextCalculator插件有一个静态方法calculateDimensions,它接受文本、样式和约束作为参数,并返回一个包含宽度和高度的TextDimensions对象。
  2. 如果text_calculator插件不存在或API不同,你需要查找并使用一个实际存在的插件,或者手动使用Flutter的TextPainter类来计算文本布局。

为了手动计算文本布局,你可以使用以下代码示例,它不使用任何第三方插件:

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Text Layout Example'),
        ),
        body: Center(
          child: TextLayoutExample(),
        ),
      ),
    );
  }
}

class TextLayoutExample extends StatefulWidget {
  @override
  _TextLayoutExampleState createState() => _TextLayoutExampleState();
}

class _TextLayoutExampleState extends State<TextLayoutExample> {
  double? textWidth;
  double? textHeight;

  @override
  void initState() {
    super.initState();
    _calculateTextLayout();
  }

  Future<void> _calculateTextLayout() async {
    final TextStyle textStyle = TextStyle(fontSize: 20, color: Colors.black);
    final String text = "Hello, this is a sample text for demonstration.";
    final TextPainter textPainter = TextPainter(
      text: TextSpan(text: text, style: textStyle),
      textAlign: TextAlign.left,
      textDirection: TextDirection.ltr,
    )..layout(minWidth: 0, maxWidth: double.infinity);

    setState(() {
      textWidth = textPainter.width;
      textHeight = textPainter.height;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text(
          "Text Width: $textWidth\nText Height: $textHeight",
          style: TextStyle(fontSize: 18),
        ),
        SizedBox(height: 20),
        Container(
          decoration: BoxDecoration(
            border: Border.all(color: Colors.black),
          ),
          constraints: BoxConstraints(
            width: textWidth ?? 100,
            height: textHeight ?? 50,
          ),
          child: Center(
            child: Text(
              "Hello, this is a sample text for demonstration.",
              style: TextStyle(fontSize: 20, color: Colors.black),
            ),
          ),
        ),
      ],
    );
  }
}

这个示例使用TextPainter类来计算文本的布局,并获取其宽度和高度。这是Flutter中处理文本布局的一种常见方法。

回到顶部